Fossil SCM

merge trunk

jan.nijtmans 2012-11-22 09:32 ticket-d17d6e5b17 merge
Commit e6a1910fa8a319943edfc1e67cd7dbeef4f2d290
+16 -8
--- src/attach.c
+++ src/attach.c
@@ -71,10 +71,11 @@
7171
const char *zFilename = db_column_text(&q, 3);
7272
const char *zComment = db_column_text(&q, 4);
7373
const char *zUser = db_column_text(&q, 5);
7474
const char *zUuid = db_column_text(&q, 6);
7575
int attachid = db_column_int(&q, 7);
76
+ const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
7677
int i;
7778
char *zUrlTail;
7879
for(i=0; zFilename[i]; i++){
7980
if( zFilename[i]=='/' && zFilename[i+1]!=0 ){
8081
zFilename = &zFilename[i+1];
@@ -115,11 +116,11 @@
115116
@ Deleted
116117
}else {
117118
@ Added
118119
}
119120
}
120
- @ by %h(zUser) on
121
+ @ by %h(zDispUser) on
121122
hyperlink_to_date(zDate, ".");
122123
free(zUrlTail);
123124
}
124125
db_finalize(&q);
125126
@ </ol>
@@ -235,10 +236,11 @@
235236
const char *aContent = P("f");
236237
const char *zName = PD("f:filename","unknown");
237238
const char *zTarget;
238239
const char *zTargetType;
239240
int szContent = atoi(PD("f:bytes","0"));
241
+ int goodCaptcha = 1;
240242
241243
if( P("cancel") ) cgi_redirect(zFrom);
242244
if( zPage && zTkt ) fossil_redirect_home();
243245
if( zPage==0 && zTkt==0 ) fossil_redirect_home();
244246
login_check_credentials();
@@ -263,11 +265,11 @@
263265
}
264266
if( zFrom==0 ) zFrom = mprintf("%s/home", g.zTop);
265267
if( P("cancel") ){
266268
cgi_redirect(zFrom);
267269
}
268
- if( P("ok") && szContent>0 ){
270
+ if( P("ok") && szContent>0 && (goodCaptcha = captcha_is_correct()) ){
269271
Blob content;
270272
Blob manifest;
271273
Blob cksum;
272274
char *zUUID;
273275
const char *zComment;
@@ -316,13 +318,16 @@
316318
assert( blob_is_reset(&manifest) );
317319
db_end_transaction(0);
318320
cgi_redirect(zFrom);
319321
}
320322
style_header("Add Attachment");
323
+ if( !goodCaptcha ){
324
+ @ <p class="generalError">Error: Incorrect security code.</p>
325
+ }
321326
@ <h2>Add Attachment To %s(zTargetType)</h2>
322
- @ <form action="%s(g.zTop)/attachadd" method="post"
323
- @ enctype="multipart/form-data"><div>
327
+ form_begin("enctype='multipart/form-data'", "%R/attachadd");
328
+ @ <div>
324329
@ File to Attach:
325330
@ <input type="file" name="f" size="60" /><br />
326331
@ Description:<br />
327332
@ <textarea name="comment" cols="80" rows="5" wrap="virtual"></textarea><br />
328333
if( zTkt ){
@@ -331,11 +336,13 @@
331336
@ <input type="hidden" name="page" value="%h(zPage)" />
332337
}
333338
@ <input type="hidden" name="from" value="%h(zFrom)" />
334339
@ <input type="submit" name="ok" value="Add Attachment" />
335340
@ <input type="submit" name="cancel" value="Cancel" />
336
- @ </div></form>
341
+ @ </div>
342
+ captcha_generate();
343
+ @ </form>
337344
style_footer();
338345
}
339346
340347
/*
341348
** WEBPAGE: ainfo
@@ -432,11 +439,11 @@
432439
}
433440
434441
if( P("del")
435442
&& ((zTktUuid && g.perm.WrTkt) || (zWikiName && g.perm.WrWiki))
436443
){
437
- @ <form method="post" action="%R/ainfo/%s(zUuid)">
444
+ form_begin(0, "%R/ainfo/%s", zUuid);
438445
@ <p>Confirm you want to delete the attachment shown below.
439446
@ <input type="submit" name="confirm" value="Confirm">
440447
@ </form>
441448
}
442449
@@ -496,11 +503,11 @@
496503
@ </table>
497504
498505
if( isModerator && modPending ){
499506
@ <div class="section">Moderation</div>
500507
@ <blockquote>
501
- @ <form method="POST" action="%R/ainfo/%s(zUuid)">
508
+ form_begin(0, "%R/ainfo/%s", zUuid);
502509
@ <label><input type="radio" name="modaction" value="delete">
503510
@ Delete this change</label><br />
504511
@ <label><input type="radio" name="modaction" value="approve">
505512
@ Approve this change</label><br />
506513
@ <input type="submit" value="Submit">
@@ -557,17 +564,18 @@
557564
const char *zDate = db_column_text(&q, 0);
558565
const char *zFile = db_column_text(&q, 1);
559566
const char *zUser = db_column_text(&q, 2);
560567
const char *zUuid = db_column_text(&q, 3);
561568
const char *zSrc = db_column_text(&q, 4);
569
+ const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
562570
if( cnt==0 ){
563571
@ %s(zHeader)
564572
}
565573
cnt++;
566574
@ <li>
567575
@ %z(href("%R/artifact/%s",zSrc))%h(zFile)</a>
568
- @ added by %h(zUser) on
576
+ @ added by %h(zDispUser) on
569577
hyperlink_to_date(zDate, ".");
570578
@ [%z(href("%R/ainfo/%s",zUuid))details</a>]
571579
@ </li>
572580
}
573581
if( cnt ){
574582
--- src/attach.c
+++ src/attach.c
@@ -71,10 +71,11 @@
71 const char *zFilename = db_column_text(&q, 3);
72 const char *zComment = db_column_text(&q, 4);
73 const char *zUser = db_column_text(&q, 5);
74 const char *zUuid = db_column_text(&q, 6);
75 int attachid = db_column_int(&q, 7);
 
76 int i;
77 char *zUrlTail;
78 for(i=0; zFilename[i]; i++){
79 if( zFilename[i]=='/' && zFilename[i+1]!=0 ){
80 zFilename = &zFilename[i+1];
@@ -115,11 +116,11 @@
115 @ Deleted
116 }else {
117 @ Added
118 }
119 }
120 @ by %h(zUser) on
121 hyperlink_to_date(zDate, ".");
122 free(zUrlTail);
123 }
124 db_finalize(&q);
125 @ </ol>
@@ -235,10 +236,11 @@
235 const char *aContent = P("f");
236 const char *zName = PD("f:filename","unknown");
237 const char *zTarget;
238 const char *zTargetType;
239 int szContent = atoi(PD("f:bytes","0"));
 
240
241 if( P("cancel") ) cgi_redirect(zFrom);
242 if( zPage && zTkt ) fossil_redirect_home();
243 if( zPage==0 && zTkt==0 ) fossil_redirect_home();
244 login_check_credentials();
@@ -263,11 +265,11 @@
263 }
264 if( zFrom==0 ) zFrom = mprintf("%s/home", g.zTop);
265 if( P("cancel") ){
266 cgi_redirect(zFrom);
267 }
268 if( P("ok") && szContent>0 ){
269 Blob content;
270 Blob manifest;
271 Blob cksum;
272 char *zUUID;
273 const char *zComment;
@@ -316,13 +318,16 @@
316 assert( blob_is_reset(&manifest) );
317 db_end_transaction(0);
318 cgi_redirect(zFrom);
319 }
320 style_header("Add Attachment");
 
 
 
321 @ <h2>Add Attachment To %s(zTargetType)</h2>
322 @ <form action="%s(g.zTop)/attachadd" method="post"
323 @ enctype="multipart/form-data"><div>
324 @ File to Attach:
325 @ <input type="file" name="f" size="60" /><br />
326 @ Description:<br />
327 @ <textarea name="comment" cols="80" rows="5" wrap="virtual"></textarea><br />
328 if( zTkt ){
@@ -331,11 +336,13 @@
331 @ <input type="hidden" name="page" value="%h(zPage)" />
332 }
333 @ <input type="hidden" name="from" value="%h(zFrom)" />
334 @ <input type="submit" name="ok" value="Add Attachment" />
335 @ <input type="submit" name="cancel" value="Cancel" />
336 @ </div></form>
 
 
337 style_footer();
338 }
339
340 /*
341 ** WEBPAGE: ainfo
@@ -432,11 +439,11 @@
432 }
433
434 if( P("del")
435 && ((zTktUuid && g.perm.WrTkt) || (zWikiName && g.perm.WrWiki))
436 ){
437 @ <form method="post" action="%R/ainfo/%s(zUuid)">
438 @ <p>Confirm you want to delete the attachment shown below.
439 @ <input type="submit" name="confirm" value="Confirm">
440 @ </form>
441 }
442
@@ -496,11 +503,11 @@
496 @ </table>
497
498 if( isModerator && modPending ){
499 @ <div class="section">Moderation</div>
500 @ <blockquote>
501 @ <form method="POST" action="%R/ainfo/%s(zUuid)">
502 @ <label><input type="radio" name="modaction" value="delete">
503 @ Delete this change</label><br />
504 @ <label><input type="radio" name="modaction" value="approve">
505 @ Approve this change</label><br />
506 @ <input type="submit" value="Submit">
@@ -557,17 +564,18 @@
557 const char *zDate = db_column_text(&q, 0);
558 const char *zFile = db_column_text(&q, 1);
559 const char *zUser = db_column_text(&q, 2);
560 const char *zUuid = db_column_text(&q, 3);
561 const char *zSrc = db_column_text(&q, 4);
 
562 if( cnt==0 ){
563 @ %s(zHeader)
564 }
565 cnt++;
566 @ <li>
567 @ %z(href("%R/artifact/%s",zSrc))%h(zFile)</a>
568 @ added by %h(zUser) on
569 hyperlink_to_date(zDate, ".");
570 @ [%z(href("%R/ainfo/%s",zUuid))details</a>]
571 @ </li>
572 }
573 if( cnt ){
574
--- src/attach.c
+++ src/attach.c
@@ -71,10 +71,11 @@
71 const char *zFilename = db_column_text(&q, 3);
72 const char *zComment = db_column_text(&q, 4);
73 const char *zUser = db_column_text(&q, 5);
74 const char *zUuid = db_column_text(&q, 6);
75 int attachid = db_column_int(&q, 7);
76 const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
77 int i;
78 char *zUrlTail;
79 for(i=0; zFilename[i]; i++){
80 if( zFilename[i]=='/' && zFilename[i+1]!=0 ){
81 zFilename = &zFilename[i+1];
@@ -115,11 +116,11 @@
116 @ Deleted
117 }else {
118 @ Added
119 }
120 }
121 @ by %h(zDispUser) on
122 hyperlink_to_date(zDate, ".");
123 free(zUrlTail);
124 }
125 db_finalize(&q);
126 @ </ol>
@@ -235,10 +236,11 @@
236 const char *aContent = P("f");
237 const char *zName = PD("f:filename","unknown");
238 const char *zTarget;
239 const char *zTargetType;
240 int szContent = atoi(PD("f:bytes","0"));
241 int goodCaptcha = 1;
242
243 if( P("cancel") ) cgi_redirect(zFrom);
244 if( zPage && zTkt ) fossil_redirect_home();
245 if( zPage==0 && zTkt==0 ) fossil_redirect_home();
246 login_check_credentials();
@@ -263,11 +265,11 @@
265 }
266 if( zFrom==0 ) zFrom = mprintf("%s/home", g.zTop);
267 if( P("cancel") ){
268 cgi_redirect(zFrom);
269 }
270 if( P("ok") && szContent>0 && (goodCaptcha = captcha_is_correct()) ){
271 Blob content;
272 Blob manifest;
273 Blob cksum;
274 char *zUUID;
275 const char *zComment;
@@ -316,13 +318,16 @@
318 assert( blob_is_reset(&manifest) );
319 db_end_transaction(0);
320 cgi_redirect(zFrom);
321 }
322 style_header("Add Attachment");
323 if( !goodCaptcha ){
324 @ <p class="generalError">Error: Incorrect security code.</p>
325 }
326 @ <h2>Add Attachment To %s(zTargetType)</h2>
327 form_begin("enctype='multipart/form-data'", "%R/attachadd");
328 @ <div>
329 @ File to Attach:
330 @ <input type="file" name="f" size="60" /><br />
331 @ Description:<br />
332 @ <textarea name="comment" cols="80" rows="5" wrap="virtual"></textarea><br />
333 if( zTkt ){
@@ -331,11 +336,13 @@
336 @ <input type="hidden" name="page" value="%h(zPage)" />
337 }
338 @ <input type="hidden" name="from" value="%h(zFrom)" />
339 @ <input type="submit" name="ok" value="Add Attachment" />
340 @ <input type="submit" name="cancel" value="Cancel" />
341 @ </div>
342 captcha_generate();
343 @ </form>
344 style_footer();
345 }
346
347 /*
348 ** WEBPAGE: ainfo
@@ -432,11 +439,11 @@
439 }
440
441 if( P("del")
442 && ((zTktUuid && g.perm.WrTkt) || (zWikiName && g.perm.WrWiki))
443 ){
444 form_begin(0, "%R/ainfo/%s", zUuid);
445 @ <p>Confirm you want to delete the attachment shown below.
446 @ <input type="submit" name="confirm" value="Confirm">
447 @ </form>
448 }
449
@@ -496,11 +503,11 @@
503 @ </table>
504
505 if( isModerator && modPending ){
506 @ <div class="section">Moderation</div>
507 @ <blockquote>
508 form_begin(0, "%R/ainfo/%s", zUuid);
509 @ <label><input type="radio" name="modaction" value="delete">
510 @ Delete this change</label><br />
511 @ <label><input type="radio" name="modaction" value="approve">
512 @ Approve this change</label><br />
513 @ <input type="submit" value="Submit">
@@ -557,17 +564,18 @@
564 const char *zDate = db_column_text(&q, 0);
565 const char *zFile = db_column_text(&q, 1);
566 const char *zUser = db_column_text(&q, 2);
567 const char *zUuid = db_column_text(&q, 3);
568 const char *zSrc = db_column_text(&q, 4);
569 const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
570 if( cnt==0 ){
571 @ %s(zHeader)
572 }
573 cnt++;
574 @ <li>
575 @ %z(href("%R/artifact/%s",zSrc))%h(zFile)</a>
576 @ added by %h(zDispUser) on
577 hyperlink_to_date(zDate, ".");
578 @ [%z(href("%R/ainfo/%s",zUuid))details</a>]
579 @ </li>
580 }
581 if( cnt ){
582
+1 -1
--- src/branch.c
+++ src/branch.c
@@ -176,11 +176,11 @@
176176
177177
/* Commit */
178178
db_end_transaction(0);
179179
180180
/* Do an autosync push, if requested */
181
- if( !isPrivate ) autosync(AUTOSYNC_PUSH);
181
+ if( !isPrivate ) autosync(SYNC_PUSH);
182182
}
183183
184184
/*
185185
** Prepare a query that will list branches.
186186
**
187187
--- src/branch.c
+++ src/branch.c
@@ -176,11 +176,11 @@
176
177 /* Commit */
178 db_end_transaction(0);
179
180 /* Do an autosync push, if requested */
181 if( !isPrivate ) autosync(AUTOSYNC_PUSH);
182 }
183
184 /*
185 ** Prepare a query that will list branches.
186 **
187
--- src/branch.c
+++ src/branch.c
@@ -176,11 +176,11 @@
176
177 /* Commit */
178 db_end_transaction(0);
179
180 /* Do an autosync push, if requested */
181 if( !isPrivate ) autosync(SYNC_PUSH);
182 }
183
184 /*
185 ** Prepare a query that will list branches.
186 **
187
--- src/captcha.c
+++ src/captcha.c
@@ -438,5 +438,70 @@
438438
z = blob_buffer(&b);
439439
memcpy(zRes, z, 8);
440440
zRes[8] = 0;
441441
return zRes;
442442
}
443
+
444
+/*
445
+** Return true if a CAPTCHA is required for editing wiki or tickets or for
446
+** adding attachments.
447
+**
448
+** A CAPTCHA is required in those cases if the user is not logged in (if they
449
+** are user "nobody") and if the "require-captcha" setting is true. The
450
+** "require-captcha" setting is controlled on the Admin/Access page. It
451
+** defaults to true.
452
+*/
453
+int captcha_needed(void){
454
+ if( g.zLogin!=0 ) return 0;
455
+ return db_get_boolean("require-captcha", 1);
456
+}
457
+
458
+/*
459
+** If a captcha is required but the correct captcha code is not supplied
460
+** in the query parameters, then return false (0).
461
+**
462
+** If no captcha is required or if the correct captcha is supplied, return
463
+** true (non-zero).
464
+**
465
+** The query parameters examined are "captchaseed" for the seed value and
466
+** "captcha" for text that the user types in response to the captcha prompt.
467
+*/
468
+int captcha_is_correct(void){
469
+ const char *zSeed;
470
+ const char *zEntered;
471
+ const char *zDecode;
472
+ if( !captcha_needed() ){
473
+ return 1; /* No captcha needed */
474
+ }
475
+ zSeed = P("captchaseed");
476
+ if( zSeed==0 ) return 0;
477
+ zEntered = P("captcha");
478
+ if( zEntered==0 || strlen(zEntered)!=8 ) return 0;
479
+ zDecode = captcha_decode((unsigned int)atoi(zSeed));
480
+ if( strcmp(zDecode,zEntered)!=0 ) return 0;
481
+ return 1;
482
+}
483
+
484
+/*
485
+** Generate a captcha display together with the necessary hidden parameter
486
+** for the seed and the entry box into which the user will type the text of
487
+** the captcha. This is typically done at the very bottom of a form.
488
+**
489
+** This routine is a no-op if no captcha is required.
490
+*/
491
+void captcha_generate(void){
492
+ unsigned int uSeed;
493
+ const char *zDecoded;
494
+ char *zCaptcha;
495
+
496
+ if( !captcha_needed() ) return;
497
+ uSeed = captcha_seed();
498
+ zDecoded = captcha_decode(uSeed);
499
+ zCaptcha = captcha_render(zDecoded);
500
+ @ <div class="captcha"><table class="captcha"><tr><td><pre>
501
+ @ %h(zCaptcha)
502
+ @ </pre>
503
+ @ Enter security code shown above:
504
+ @ <input type="hidden" name="captchaseed" value="%u(uSeed)" />
505
+ @ <input type="text" name="captcha" size=8 />
506
+ @ </td></tr></table></div>
507
+}
443508
--- src/captcha.c
+++ src/captcha.c
@@ -438,5 +438,70 @@
438 z = blob_buffer(&b);
439 memcpy(zRes, z, 8);
440 zRes[8] = 0;
441 return zRes;
442 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
443
--- src/captcha.c
+++ src/captcha.c
@@ -438,5 +438,70 @@
438 z = blob_buffer(&b);
439 memcpy(zRes, z, 8);
440 zRes[8] = 0;
441 return zRes;
442 }
443
444 /*
445 ** Return true if a CAPTCHA is required for editing wiki or tickets or for
446 ** adding attachments.
447 **
448 ** A CAPTCHA is required in those cases if the user is not logged in (if they
449 ** are user "nobody") and if the "require-captcha" setting is true. The
450 ** "require-captcha" setting is controlled on the Admin/Access page. It
451 ** defaults to true.
452 */
453 int captcha_needed(void){
454 if( g.zLogin!=0 ) return 0;
455 return db_get_boolean("require-captcha", 1);
456 }
457
458 /*
459 ** If a captcha is required but the correct captcha code is not supplied
460 ** in the query parameters, then return false (0).
461 **
462 ** If no captcha is required or if the correct captcha is supplied, return
463 ** true (non-zero).
464 **
465 ** The query parameters examined are "captchaseed" for the seed value and
466 ** "captcha" for text that the user types in response to the captcha prompt.
467 */
468 int captcha_is_correct(void){
469 const char *zSeed;
470 const char *zEntered;
471 const char *zDecode;
472 if( !captcha_needed() ){
473 return 1; /* No captcha needed */
474 }
475 zSeed = P("captchaseed");
476 if( zSeed==0 ) return 0;
477 zEntered = P("captcha");
478 if( zEntered==0 || strlen(zEntered)!=8 ) return 0;
479 zDecode = captcha_decode((unsigned int)atoi(zSeed));
480 if( strcmp(zDecode,zEntered)!=0 ) return 0;
481 return 1;
482 }
483
484 /*
485 ** Generate a captcha display together with the necessary hidden parameter
486 ** for the seed and the entry box into which the user will type the text of
487 ** the captcha. This is typically done at the very bottom of a form.
488 **
489 ** This routine is a no-op if no captcha is required.
490 */
491 void captcha_generate(void){
492 unsigned int uSeed;
493 const char *zDecoded;
494 char *zCaptcha;
495
496 if( !captcha_needed() ) return;
497 uSeed = captcha_seed();
498 zDecoded = captcha_decode(uSeed);
499 zCaptcha = captcha_render(zDecoded);
500 @ <div class="captcha"><table class="captcha"><tr><td><pre>
501 @ %h(zCaptcha)
502 @ </pre>
503 @ Enter security code shown above:
504 @ <input type="hidden" name="captchaseed" value="%u(uSeed)" />
505 @ <input type="text" name="captcha" size=8 />
506 @ </td></tr></table></div>
507 }
508
+2 -2
--- src/checkin.c
+++ src/checkin.c
@@ -1108,11 +1108,11 @@
11081108
11091109
/*
11101110
** Autosync if autosync is enabled and this is not a private check-in.
11111111
*/
11121112
if( !g.markPrivate ){
1113
- if( autosync(AUTOSYNC_PULL) ){
1113
+ if( autosync(SYNC_PULL) ){
11141114
blob_zero(&ans);
11151115
prompt_user("continue in spite of sync failure (y/N)? ", &ans);
11161116
cReply = blob_str(&ans)[0];
11171117
if( cReply!='y' && cReply!='Y' ){
11181118
fossil_exit(1);
@@ -1459,11 +1459,11 @@
14591459
exit(1);
14601460
}
14611461
db_end_transaction(0);
14621462
14631463
if( !g.markPrivate ){
1464
- autosync(AUTOSYNC_PUSH);
1464
+ autosync(SYNC_PUSH);
14651465
}
14661466
if( count_nonbranch_children(vid)>1 ){
14671467
fossil_print("**** warning: a fork has occurred *****\n");
14681468
}
14691469
}
14701470
--- src/checkin.c
+++ src/checkin.c
@@ -1108,11 +1108,11 @@
1108
1109 /*
1110 ** Autosync if autosync is enabled and this is not a private check-in.
1111 */
1112 if( !g.markPrivate ){
1113 if( autosync(AUTOSYNC_PULL) ){
1114 blob_zero(&ans);
1115 prompt_user("continue in spite of sync failure (y/N)? ", &ans);
1116 cReply = blob_str(&ans)[0];
1117 if( cReply!='y' && cReply!='Y' ){
1118 fossil_exit(1);
@@ -1459,11 +1459,11 @@
1459 exit(1);
1460 }
1461 db_end_transaction(0);
1462
1463 if( !g.markPrivate ){
1464 autosync(AUTOSYNC_PUSH);
1465 }
1466 if( count_nonbranch_children(vid)>1 ){
1467 fossil_print("**** warning: a fork has occurred *****\n");
1468 }
1469 }
1470
--- src/checkin.c
+++ src/checkin.c
@@ -1108,11 +1108,11 @@
1108
1109 /*
1110 ** Autosync if autosync is enabled and this is not a private check-in.
1111 */
1112 if( !g.markPrivate ){
1113 if( autosync(SYNC_PULL) ){
1114 blob_zero(&ans);
1115 prompt_user("continue in spite of sync failure (y/N)? ", &ans);
1116 cReply = blob_str(&ans)[0];
1117 if( cReply!='y' && cReply!='Y' ){
1118 fossil_exit(1);
@@ -1459,11 +1459,11 @@
1459 exit(1);
1460 }
1461 db_end_transaction(0);
1462
1463 if( !g.markPrivate ){
1464 autosync(SYNC_PUSH);
1465 }
1466 if( count_nonbranch_children(vid)>1 ){
1467 fossil_print("**** warning: a fork has occurred *****\n");
1468 }
1469 }
1470
+4 -4
--- src/clone.c
+++ src/clone.c
@@ -99,15 +99,15 @@
9999
** See also: init
100100
*/
101101
void clone_cmd(void){
102102
char *zPassword;
103103
const char *zDefaultUser; /* Optional name of the default user */
104
- const char *zPw; /* The user clone password */
104
+ const char *zPw; /* The user clone password */
105105
int nErr = 0;
106
- int bPrivate; /* Also clone private branches */
106
+ int bPrivate = 0; /* Also clone private branches */
107107
108
- bPrivate = find_option("private",0,0)!=0;
108
+ if( find_option("private",0,0)!=0 ) bPrivate = SYNC_PRIVATE;
109109
url_proxy_options();
110110
if( g.argc < 4 ){
111111
usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY");
112112
}
113113
db_open_config(0);
@@ -162,11 +162,11 @@
162162
" VALUES('server-code', lower(hex(randomblob(20))), now());"
163163
);
164164
url_enable_proxy(0);
165165
url_get_password_if_needed();
166166
g.xlinkClusterOnly = 1;
167
- nErr = client_sync(0,0,1,bPrivate,CONFIGSET_ALL,0);
167
+ nErr = client_sync(SYNC_CLONE | bPrivate,CONFIGSET_ALL,0);
168168
g.xlinkClusterOnly = 0;
169169
verify_cancel();
170170
db_end_transaction(0);
171171
db_close(1);
172172
if( nErr ){
173173
--- src/clone.c
+++ src/clone.c
@@ -99,15 +99,15 @@
99 ** See also: init
100 */
101 void clone_cmd(void){
102 char *zPassword;
103 const char *zDefaultUser; /* Optional name of the default user */
104 const char *zPw; /* The user clone password */
105 int nErr = 0;
106 int bPrivate; /* Also clone private branches */
107
108 bPrivate = find_option("private",0,0)!=0;
109 url_proxy_options();
110 if( g.argc < 4 ){
111 usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY");
112 }
113 db_open_config(0);
@@ -162,11 +162,11 @@
162 " VALUES('server-code', lower(hex(randomblob(20))), now());"
163 );
164 url_enable_proxy(0);
165 url_get_password_if_needed();
166 g.xlinkClusterOnly = 1;
167 nErr = client_sync(0,0,1,bPrivate,CONFIGSET_ALL,0);
168 g.xlinkClusterOnly = 0;
169 verify_cancel();
170 db_end_transaction(0);
171 db_close(1);
172 if( nErr ){
173
--- src/clone.c
+++ src/clone.c
@@ -99,15 +99,15 @@
99 ** See also: init
100 */
101 void clone_cmd(void){
102 char *zPassword;
103 const char *zDefaultUser; /* Optional name of the default user */
104 const char *zPw; /* The user clone password */
105 int nErr = 0;
106 int bPrivate = 0; /* Also clone private branches */
107
108 if( find_option("private",0,0)!=0 ) bPrivate = SYNC_PRIVATE;
109 url_proxy_options();
110 if( g.argc < 4 ){
111 usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY");
112 }
113 db_open_config(0);
@@ -162,11 +162,11 @@
162 " VALUES('server-code', lower(hex(randomblob(20))), now());"
163 );
164 url_enable_proxy(0);
165 url_get_password_if_needed();
166 g.xlinkClusterOnly = 1;
167 nErr = client_sync(SYNC_CLONE | bPrivate,CONFIGSET_ALL,0);
168 g.xlinkClusterOnly = 0;
169 verify_cancel();
170 db_end_transaction(0);
171 db_close(1);
172 if( nErr ){
173
+3 -3
--- src/configure.c
+++ src/configure.c
@@ -907,15 +907,15 @@
907907
user_select();
908908
url_enable_proxy("via proxy: ");
909909
if( legacyFlag ) mask |= CONFIGSET_OLDFORMAT;
910910
if( overwriteFlag ) mask |= CONFIGSET_OVERWRITE;
911911
if( strncmp(zMethod, "push", n)==0 ){
912
- client_sync(0,0,0,0,0,mask);
912
+ client_sync(0,0,(unsigned)mask);
913913
}else if( strncmp(zMethod, "pull", n)==0 ){
914
- client_sync(0,0,0,0,mask,0);
914
+ client_sync(0,(unsigned)mask,0);
915915
}else{
916
- client_sync(0,0,0,0,mask,mask);
916
+ client_sync(0,(unsigned)mask,(unsigned)mask);
917917
}
918918
}else
919919
if( strncmp(zMethod, "reset", n)==0 ){
920920
int mask, i;
921921
char *zBackup;
922922
--- src/configure.c
+++ src/configure.c
@@ -907,15 +907,15 @@
907 user_select();
908 url_enable_proxy("via proxy: ");
909 if( legacyFlag ) mask |= CONFIGSET_OLDFORMAT;
910 if( overwriteFlag ) mask |= CONFIGSET_OVERWRITE;
911 if( strncmp(zMethod, "push", n)==0 ){
912 client_sync(0,0,0,0,0,mask);
913 }else if( strncmp(zMethod, "pull", n)==0 ){
914 client_sync(0,0,0,0,mask,0);
915 }else{
916 client_sync(0,0,0,0,mask,mask);
917 }
918 }else
919 if( strncmp(zMethod, "reset", n)==0 ){
920 int mask, i;
921 char *zBackup;
922
--- src/configure.c
+++ src/configure.c
@@ -907,15 +907,15 @@
907 user_select();
908 url_enable_proxy("via proxy: ");
909 if( legacyFlag ) mask |= CONFIGSET_OLDFORMAT;
910 if( overwriteFlag ) mask |= CONFIGSET_OVERWRITE;
911 if( strncmp(zMethod, "push", n)==0 ){
912 client_sync(0,0,(unsigned)mask);
913 }else if( strncmp(zMethod, "pull", n)==0 ){
914 client_sync(0,(unsigned)mask,0);
915 }else{
916 client_sync(0,(unsigned)mask,(unsigned)mask);
917 }
918 }else
919 if( strncmp(zMethod, "reset", n)==0 ){
920 int mask, i;
921 char *zBackup;
922
--- src/descendants.c
+++ src/descendants.c
@@ -506,6 +506,5 @@
506506
db_finalize(&q);
507507
db_finalize(&ins);
508508
bag_clear(&seen);
509509
bag_clear(&pending);
510510
}
511
-
512511
--- src/descendants.c
+++ src/descendants.c
@@ -506,6 +506,5 @@
506 db_finalize(&q);
507 db_finalize(&ins);
508 bag_clear(&seen);
509 bag_clear(&pending);
510 }
511
512
--- src/descendants.c
+++ src/descendants.c
@@ -506,6 +506,5 @@
506 db_finalize(&q);
507 db_finalize(&ins);
508 bag_clear(&seen);
509 bag_clear(&pending);
510 }
 
511
+34
--- src/encode.c
+++ src/encode.c
@@ -83,10 +83,44 @@
8383
zIn++;
8484
}
8585
zOut[i] = 0;
8686
return zOut;
8787
}
88
+
89
+/*
90
+** Append HTML-escaped text to a Blob.
91
+*/
92
+void htmlize_to_blob(Blob *p, const char *zIn, int n){
93
+ int c, i, j;
94
+ if( n<0 ) n = strlen(zIn);
95
+ for(i=j=0; i<n; i++){
96
+ c = zIn[i];
97
+ switch( c ){
98
+ case '<':
99
+ if( j<i ) blob_append(p, zIn+j, i-j);
100
+ blob_append(p, "&lt;", 4);
101
+ j = i+1;
102
+ break;
103
+ case '>':
104
+ if( j<i ) blob_append(p, zIn+j, i-j);
105
+ blob_append(p, "&gt;", 4);
106
+ j = i+1;
107
+ break;
108
+ case '&':
109
+ if( j<i ) blob_append(p, zIn+j, i-j);
110
+ blob_append(p, "&amp;", 5);
111
+ j = i+1;
112
+ break;
113
+ case '"':
114
+ if( j<i ) blob_append(p, zIn+j, i-j);
115
+ blob_append(p, "&quot;", 6);
116
+ j = i+1;
117
+ break;
118
+ }
119
+ }
120
+ if( j<i ) blob_append(p, zIn+j, i-j);
121
+}
88122
89123
90124
/*
91125
** Encode a string for HTTP. This means converting lots of
92126
** characters into the "%HH" where H is a hex digit. It also
93127
--- src/encode.c
+++ src/encode.c
@@ -83,10 +83,44 @@
83 zIn++;
84 }
85 zOut[i] = 0;
86 return zOut;
87 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
89
90 /*
91 ** Encode a string for HTTP. This means converting lots of
92 ** characters into the "%HH" where H is a hex digit. It also
93
--- src/encode.c
+++ src/encode.c
@@ -83,10 +83,44 @@
83 zIn++;
84 }
85 zOut[i] = 0;
86 return zOut;
87 }
88
89 /*
90 ** Append HTML-escaped text to a Blob.
91 */
92 void htmlize_to_blob(Blob *p, const char *zIn, int n){
93 int c, i, j;
94 if( n<0 ) n = strlen(zIn);
95 for(i=j=0; i<n; i++){
96 c = zIn[i];
97 switch( c ){
98 case '<':
99 if( j<i ) blob_append(p, zIn+j, i-j);
100 blob_append(p, "&lt;", 4);
101 j = i+1;
102 break;
103 case '>':
104 if( j<i ) blob_append(p, zIn+j, i-j);
105 blob_append(p, "&gt;", 4);
106 j = i+1;
107 break;
108 case '&':
109 if( j<i ) blob_append(p, zIn+j, i-j);
110 blob_append(p, "&amp;", 5);
111 j = i+1;
112 break;
113 case '"':
114 if( j<i ) blob_append(p, zIn+j, i-j);
115 blob_append(p, "&quot;", 6);
116 j = i+1;
117 break;
118 }
119 }
120 if( j<i ) blob_append(p, zIn+j, i-j);
121 }
122
123
124 /*
125 ** Encode a string for HTTP. This means converting lots of
126 ** characters into the "%HH" where H is a hex digit. It also
127
+1 -1
--- src/event.c
+++ src/event.c
@@ -365,11 +365,11 @@
365365
}else{
366366
@ <tr><td>
367367
}
368368
blob_zero(&com);
369369
blob_append(&com, zComment, -1);
370
- wiki_convert(&com, 0, WIKI_INLINE);
370
+ wiki_convert(&com, 0, WIKI_INLINE|WIKI_NOBADLINKS);
371371
@ </td></tr></table>
372372
@ </blockquote>
373373
@ <p><b>Page content preview:</b><p>
374374
@ <blockquote>
375375
blob_zero(&event);
376376
--- src/event.c
+++ src/event.c
@@ -365,11 +365,11 @@
365 }else{
366 @ <tr><td>
367 }
368 blob_zero(&com);
369 blob_append(&com, zComment, -1);
370 wiki_convert(&com, 0, WIKI_INLINE);
371 @ </td></tr></table>
372 @ </blockquote>
373 @ <p><b>Page content preview:</b><p>
374 @ <blockquote>
375 blob_zero(&event);
376
--- src/event.c
+++ src/event.c
@@ -365,11 +365,11 @@
365 }else{
366 @ <tr><td>
367 }
368 blob_zero(&com);
369 blob_append(&com, zComment, -1);
370 wiki_convert(&com, 0, WIKI_INLINE|WIKI_NOBADLINKS);
371 @ </td></tr></table>
372 @ </blockquote>
373 @ <p><b>Page content preview:</b><p>
374 @ <blockquote>
375 blob_zero(&event);
376
+4 -4
--- src/info.c
+++ src/info.c
@@ -865,11 +865,11 @@
865865
const char *zDate = db_column_text(&q, 0);
866866
const char *zUser = db_column_text(&q, 1);
867867
const char *zUuid = db_column_text(&q, 3);
868868
const char *zTagList = db_column_text(&q, 4);
869869
Blob comment;
870
- int wikiFlags = WIKI_INLINE;
870
+ int wikiFlags = WIKI_INLINE|WIKI_NOBADLINKS;
871871
if( db_get_boolean("timeline-block-markup", 0)==0 ){
872872
wikiFlags |= WIKI_NOBLOCK;
873873
}
874874
hyperlink_to_uuid(zUuid);
875875
blob_zero(&comment);
@@ -2153,11 +2153,11 @@
21532153
if( zNewColor && zNewColor[0] ){
21542154
@ <tr><td style="background-color: %h(zNewColor);">
21552155
}else{
21562156
@ <tr><td>
21572157
}
2158
- wiki_convert(&comment, 0, WIKI_INLINE);
2158
+ wiki_convert(&comment, 0, WIKI_INLINE|WIKI_NOBADLINKS);
21592159
blob_zero(&suffix);
21602160
blob_appendf(&suffix, "(user: %h", zNewUser);
21612161
db_prepare(&q, "SELECT substr(tagname,5) FROM tagxref, tag"
21622162
" WHERE tagname GLOB 'sym-*' AND tagxref.rid=%d"
21632163
" AND tagtype>1 AND tag.tagid=tagxref.tagid",
@@ -2183,13 +2183,13 @@
21832183
@ <hr />
21842184
blob_reset(&suffix);
21852185
}
21862186
@ <p>Make changes to attributes of check-in
21872187
@ [%z(href("%R/ci/%s",zUuid))%s(zUuid)</a>]:</p>
2188
- @ <form action="%s(g.zTop)/ci_edit" method="post"><div>
2188
+ form_begin(0, "%R/ci_edit");
21892189
login_insert_csrf_secret();
2190
- @ <input type="hidden" name="r" value="%S(zUuid)" />
2190
+ @ <div><input type="hidden" name="r" value="%S(zUuid)" />
21912191
@ <table border="0" cellspacing="10">
21922192
21932193
@ <tr><td align="right" valign="top"><b>User:</b></td>
21942194
@ <td valign="top">
21952195
@ <input type="text" name="u" size="20" value="%h(zNewUser)" />
21962196
--- src/info.c
+++ src/info.c
@@ -865,11 +865,11 @@
865 const char *zDate = db_column_text(&q, 0);
866 const char *zUser = db_column_text(&q, 1);
867 const char *zUuid = db_column_text(&q, 3);
868 const char *zTagList = db_column_text(&q, 4);
869 Blob comment;
870 int wikiFlags = WIKI_INLINE;
871 if( db_get_boolean("timeline-block-markup", 0)==0 ){
872 wikiFlags |= WIKI_NOBLOCK;
873 }
874 hyperlink_to_uuid(zUuid);
875 blob_zero(&comment);
@@ -2153,11 +2153,11 @@
2153 if( zNewColor && zNewColor[0] ){
2154 @ <tr><td style="background-color: %h(zNewColor);">
2155 }else{
2156 @ <tr><td>
2157 }
2158 wiki_convert(&comment, 0, WIKI_INLINE);
2159 blob_zero(&suffix);
2160 blob_appendf(&suffix, "(user: %h", zNewUser);
2161 db_prepare(&q, "SELECT substr(tagname,5) FROM tagxref, tag"
2162 " WHERE tagname GLOB 'sym-*' AND tagxref.rid=%d"
2163 " AND tagtype>1 AND tag.tagid=tagxref.tagid",
@@ -2183,13 +2183,13 @@
2183 @ <hr />
2184 blob_reset(&suffix);
2185 }
2186 @ <p>Make changes to attributes of check-in
2187 @ [%z(href("%R/ci/%s",zUuid))%s(zUuid)</a>]:</p>
2188 @ <form action="%s(g.zTop)/ci_edit" method="post"><div>
2189 login_insert_csrf_secret();
2190 @ <input type="hidden" name="r" value="%S(zUuid)" />
2191 @ <table border="0" cellspacing="10">
2192
2193 @ <tr><td align="right" valign="top"><b>User:</b></td>
2194 @ <td valign="top">
2195 @ <input type="text" name="u" size="20" value="%h(zNewUser)" />
2196
--- src/info.c
+++ src/info.c
@@ -865,11 +865,11 @@
865 const char *zDate = db_column_text(&q, 0);
866 const char *zUser = db_column_text(&q, 1);
867 const char *zUuid = db_column_text(&q, 3);
868 const char *zTagList = db_column_text(&q, 4);
869 Blob comment;
870 int wikiFlags = WIKI_INLINE|WIKI_NOBADLINKS;
871 if( db_get_boolean("timeline-block-markup", 0)==0 ){
872 wikiFlags |= WIKI_NOBLOCK;
873 }
874 hyperlink_to_uuid(zUuid);
875 blob_zero(&comment);
@@ -2153,11 +2153,11 @@
2153 if( zNewColor && zNewColor[0] ){
2154 @ <tr><td style="background-color: %h(zNewColor);">
2155 }else{
2156 @ <tr><td>
2157 }
2158 wiki_convert(&comment, 0, WIKI_INLINE|WIKI_NOBADLINKS);
2159 blob_zero(&suffix);
2160 blob_appendf(&suffix, "(user: %h", zNewUser);
2161 db_prepare(&q, "SELECT substr(tagname,5) FROM tagxref, tag"
2162 " WHERE tagname GLOB 'sym-*' AND tagxref.rid=%d"
2163 " AND tagtype>1 AND tag.tagid=tagxref.tagid",
@@ -2183,13 +2183,13 @@
2183 @ <hr />
2184 blob_reset(&suffix);
2185 }
2186 @ <p>Make changes to attributes of check-in
2187 @ [%z(href("%R/ci/%s",zUuid))%s(zUuid)</a>]:</p>
2188 form_begin(0, "%R/ci_edit");
2189 login_insert_csrf_secret();
2190 @ <div><input type="hidden" name="r" value="%S(zUuid)" />
2191 @ <table border="0" cellspacing="10">
2192
2193 @ <tr><td align="right" valign="top"><b>User:</b></td>
2194 @ <td valign="top">
2195 @ <input type="text" name="u" size="20" value="%h(zNewUser)" />
2196
--- src/json_branch.c
+++ src/json_branch.c
@@ -305,11 +305,11 @@
305305
/* Commit */
306306
db_end_transaction(0);
307307
308308
#if 0 /* Do an autosync push, if requested */
309309
/* arugable for JSON mode? */
310
- if( !g.isHTTP && !isPrivate ) autosync(AUTOSYNC_PUSH);
310
+ if( !g.isHTTP && !isPrivate ) autosync(SYNC_PUSH);
311311
#endif
312312
return 0;
313313
}
314314
315315
316316
--- src/json_branch.c
+++ src/json_branch.c
@@ -305,11 +305,11 @@
305 /* Commit */
306 db_end_transaction(0);
307
308 #if 0 /* Do an autosync push, if requested */
309 /* arugable for JSON mode? */
310 if( !g.isHTTP && !isPrivate ) autosync(AUTOSYNC_PUSH);
311 #endif
312 return 0;
313 }
314
315
316
--- src/json_branch.c
+++ src/json_branch.c
@@ -305,11 +305,11 @@
305 /* Commit */
306 db_end_transaction(0);
307
308 #if 0 /* Do an autosync push, if requested */
309 /* arugable for JSON mode? */
310 if( !g.isHTTP && !isPrivate ) autosync(SYNC_PUSH);
311 #endif
312 return 0;
313 }
314
315
316
+5 -5
--- src/login.c
+++ src/login.c
@@ -565,11 +565,11 @@
565565
style_header("Login/Logout");
566566
@ %s(zErrMsg)
567567
if( zGoto && P("anon")==0 ){
568568
@ <p>A login is required for <a href="%h(zGoto)">%h(zGoto)</a>.</p>
569569
}
570
- @ <form action="login" method="post">
570
+ form_begin(0, "%R/login");
571571
if( zGoto ){
572572
@ <input type="hidden" name="g" value="%h(zGoto)" />
573573
}
574574
@ <table class="login_out">
575575
@ <tr>
@@ -631,11 +631,11 @@
631631
632632
@ <p><input type="hidden" name="cs" value="%u(uSeed)" />
633633
@ Visitors may enter <b>anonymous</b> as the user-ID with
634634
@ the 8-character hexadecimal password shown below:</p>
635635
@ <div class="captcha"><table class="captcha"><tr><td><pre>
636
- @ %s(zCaptcha)
636
+ @ %h(zCaptcha)
637637
@ </pre></td></tr></table>
638638
if( bAutoCaptcha ) {
639639
@ <input type="button" value="Fill out captcha"
640640
@ onclick="gebi('u').value='anonymous'; gebi('p').value='%s(zDecoded)';" />
641641
}
@@ -652,11 +652,11 @@
652652
if( g.perm.Password ){
653653
@ <hr />
654654
@ <p>To change your password, enter your old password and your
655655
@ new password twice below then press the "Change Password"
656656
@ button.</p>
657
- @ <form action="login" method="post">
657
+ form_begin(0, "%R/login");
658658
@ <table>
659659
@ <tr><td class="login_out_label">Old Password:</td>
660660
@ <td><input type="password" name="p" size="30" /></td></tr>
661661
@ <tr><td class="login_out_label">New Password:</td>
662662
@ <td><input type="password" name="n1" size="30" /></td></tr>
@@ -1260,11 +1260,11 @@
12601260
uSeed = captcha_seed();
12611261
zDecoded = captcha_decode(uSeed);
12621262
zCaptcha = captcha_render(zDecoded);
12631263
12641264
/* Print out the registration form. */
1265
- @ <form action="register" method="post">
1265
+ form_begin(0, "%R/register");
12661266
if( P("g") ){
12671267
@ <input type="hidden" name="g" value="%h(P("g"))" />
12681268
}
12691269
@ <p><input type="hidden" name="cs" value="%u(uSeed)" />
12701270
@ <table class="login_out">
@@ -1290,11 +1290,11 @@
12901290
@ </tr>
12911291
@ <tr><td></td>
12921292
@ <td><input type="submit" name="new" value="Register" /></td></tr>
12931293
@ </table>
12941294
@ <div class="captcha"><table class="captcha"><tr><td><pre>
1295
- @ %s(zCaptcha)
1295
+ @ %h(zCaptcha)
12961296
@ </pre></td></tr></table>
12971297
@ </form>
12981298
style_footer();
12991299
13001300
free(zCaptcha);
13011301
--- src/login.c
+++ src/login.c
@@ -565,11 +565,11 @@
565 style_header("Login/Logout");
566 @ %s(zErrMsg)
567 if( zGoto && P("anon")==0 ){
568 @ <p>A login is required for <a href="%h(zGoto)">%h(zGoto)</a>.</p>
569 }
570 @ <form action="login" method="post">
571 if( zGoto ){
572 @ <input type="hidden" name="g" value="%h(zGoto)" />
573 }
574 @ <table class="login_out">
575 @ <tr>
@@ -631,11 +631,11 @@
631
632 @ <p><input type="hidden" name="cs" value="%u(uSeed)" />
633 @ Visitors may enter <b>anonymous</b> as the user-ID with
634 @ the 8-character hexadecimal password shown below:</p>
635 @ <div class="captcha"><table class="captcha"><tr><td><pre>
636 @ %s(zCaptcha)
637 @ </pre></td></tr></table>
638 if( bAutoCaptcha ) {
639 @ <input type="button" value="Fill out captcha"
640 @ onclick="gebi('u').value='anonymous'; gebi('p').value='%s(zDecoded)';" />
641 }
@@ -652,11 +652,11 @@
652 if( g.perm.Password ){
653 @ <hr />
654 @ <p>To change your password, enter your old password and your
655 @ new password twice below then press the "Change Password"
656 @ button.</p>
657 @ <form action="login" method="post">
658 @ <table>
659 @ <tr><td class="login_out_label">Old Password:</td>
660 @ <td><input type="password" name="p" size="30" /></td></tr>
661 @ <tr><td class="login_out_label">New Password:</td>
662 @ <td><input type="password" name="n1" size="30" /></td></tr>
@@ -1260,11 +1260,11 @@
1260 uSeed = captcha_seed();
1261 zDecoded = captcha_decode(uSeed);
1262 zCaptcha = captcha_render(zDecoded);
1263
1264 /* Print out the registration form. */
1265 @ <form action="register" method="post">
1266 if( P("g") ){
1267 @ <input type="hidden" name="g" value="%h(P("g"))" />
1268 }
1269 @ <p><input type="hidden" name="cs" value="%u(uSeed)" />
1270 @ <table class="login_out">
@@ -1290,11 +1290,11 @@
1290 @ </tr>
1291 @ <tr><td></td>
1292 @ <td><input type="submit" name="new" value="Register" /></td></tr>
1293 @ </table>
1294 @ <div class="captcha"><table class="captcha"><tr><td><pre>
1295 @ %s(zCaptcha)
1296 @ </pre></td></tr></table>
1297 @ </form>
1298 style_footer();
1299
1300 free(zCaptcha);
1301
--- src/login.c
+++ src/login.c
@@ -565,11 +565,11 @@
565 style_header("Login/Logout");
566 @ %s(zErrMsg)
567 if( zGoto && P("anon")==0 ){
568 @ <p>A login is required for <a href="%h(zGoto)">%h(zGoto)</a>.</p>
569 }
570 form_begin(0, "%R/login");
571 if( zGoto ){
572 @ <input type="hidden" name="g" value="%h(zGoto)" />
573 }
574 @ <table class="login_out">
575 @ <tr>
@@ -631,11 +631,11 @@
631
632 @ <p><input type="hidden" name="cs" value="%u(uSeed)" />
633 @ Visitors may enter <b>anonymous</b> as the user-ID with
634 @ the 8-character hexadecimal password shown below:</p>
635 @ <div class="captcha"><table class="captcha"><tr><td><pre>
636 @ %h(zCaptcha)
637 @ </pre></td></tr></table>
638 if( bAutoCaptcha ) {
639 @ <input type="button" value="Fill out captcha"
640 @ onclick="gebi('u').value='anonymous'; gebi('p').value='%s(zDecoded)';" />
641 }
@@ -652,11 +652,11 @@
652 if( g.perm.Password ){
653 @ <hr />
654 @ <p>To change your password, enter your old password and your
655 @ new password twice below then press the "Change Password"
656 @ button.</p>
657 form_begin(0, "%R/login");
658 @ <table>
659 @ <tr><td class="login_out_label">Old Password:</td>
660 @ <td><input type="password" name="p" size="30" /></td></tr>
661 @ <tr><td class="login_out_label">New Password:</td>
662 @ <td><input type="password" name="n1" size="30" /></td></tr>
@@ -1260,11 +1260,11 @@
1260 uSeed = captcha_seed();
1261 zDecoded = captcha_decode(uSeed);
1262 zCaptcha = captcha_render(zDecoded);
1263
1264 /* Print out the registration form. */
1265 form_begin(0, "%R/register");
1266 if( P("g") ){
1267 @ <input type="hidden" name="g" value="%h(P("g"))" />
1268 }
1269 @ <p><input type="hidden" name="cs" value="%u(uSeed)" />
1270 @ <table class="login_out">
@@ -1290,11 +1290,11 @@
1290 @ </tr>
1291 @ <tr><td></td>
1292 @ <td><input type="submit" name="new" value="Register" /></td></tr>
1293 @ </table>
1294 @ <div class="captcha"><table class="captcha"><tr><td><pre>
1295 @ %h(zCaptcha)
1296 @ </pre></td></tr></table>
1297 @ </form>
1298 style_footer();
1299
1300 free(zCaptcha);
1301
+4 -4
--- src/main.c
+++ src/main.c
@@ -343,11 +343,11 @@
343343
if(g.db){
344344
db_close(0);
345345
}
346346
}
347347
348
-#if defined(_WIN32) && !defined(__MINGW32__)
348
+#if defined(_WIN32)
349349
/*
350350
** Parse the command-line arguments passed to windows. We do this
351351
** ourselves to work around bugs in the command-line parsing of MinGW.
352352
** It is possible (in theory) to only use this routine when compiling
353353
** with MinGW and to use built-in command-line parsing for MSVC and
@@ -456,11 +456,11 @@
456456
}
457457
argv[argc] = NULL;
458458
*argcPtr = argc;
459459
*((WCHAR ***)argvPtr) = argv;
460460
}
461
-#endif /* defined(_WIN32) && !defined(__MINGW32__) */
461
+#endif /* defined(_WIN32) */
462462
463463
464464
/*
465465
** Convert all arguments from mbcs (or unicode) to UTF-8. Then
466466
** search g.argv for arguments "--args FILENAME". If found, then
@@ -480,17 +480,17 @@
480480
int n; /* Number of bytes in one line */
481481
char *z; /* General use string pointer */
482482
char **newArgv; /* New expanded g.argv under construction */
483483
char const * zFileName; /* input file name */
484484
FILE * zInFile; /* input FILE */
485
-#if defined(_WIN32) && !defined(__MINGW32__)
485
+#if defined(_WIN32)
486486
WCHAR buf[MAX_PATH];
487487
#endif
488488
489489
g.argc = argc;
490490
g.argv = argv;
491
-#if defined(_WIN32) && !defined(__MINGW32__)
491
+#if defined(_WIN32)
492492
parse_windows_command_line(&g.argc, &g.argv);
493493
GetModuleFileNameW(NULL, buf, MAX_PATH);
494494
g.nameOfExe = fossil_unicode_to_utf8(buf);
495495
for(i=0; i<g.argc; i++) g.argv[i] = fossil_unicode_to_utf8(g.argv[i]);
496496
#else
497497
--- src/main.c
+++ src/main.c
@@ -343,11 +343,11 @@
343 if(g.db){
344 db_close(0);
345 }
346 }
347
348 #if defined(_WIN32) && !defined(__MINGW32__)
349 /*
350 ** Parse the command-line arguments passed to windows. We do this
351 ** ourselves to work around bugs in the command-line parsing of MinGW.
352 ** It is possible (in theory) to only use this routine when compiling
353 ** with MinGW and to use built-in command-line parsing for MSVC and
@@ -456,11 +456,11 @@
456 }
457 argv[argc] = NULL;
458 *argcPtr = argc;
459 *((WCHAR ***)argvPtr) = argv;
460 }
461 #endif /* defined(_WIN32) && !defined(__MINGW32__) */
462
463
464 /*
465 ** Convert all arguments from mbcs (or unicode) to UTF-8. Then
466 ** search g.argv for arguments "--args FILENAME". If found, then
@@ -480,17 +480,17 @@
480 int n; /* Number of bytes in one line */
481 char *z; /* General use string pointer */
482 char **newArgv; /* New expanded g.argv under construction */
483 char const * zFileName; /* input file name */
484 FILE * zInFile; /* input FILE */
485 #if defined(_WIN32) && !defined(__MINGW32__)
486 WCHAR buf[MAX_PATH];
487 #endif
488
489 g.argc = argc;
490 g.argv = argv;
491 #if defined(_WIN32) && !defined(__MINGW32__)
492 parse_windows_command_line(&g.argc, &g.argv);
493 GetModuleFileNameW(NULL, buf, MAX_PATH);
494 g.nameOfExe = fossil_unicode_to_utf8(buf);
495 for(i=0; i<g.argc; i++) g.argv[i] = fossil_unicode_to_utf8(g.argv[i]);
496 #else
497
--- src/main.c
+++ src/main.c
@@ -343,11 +343,11 @@
343 if(g.db){
344 db_close(0);
345 }
346 }
347
348 #if defined(_WIN32)
349 /*
350 ** Parse the command-line arguments passed to windows. We do this
351 ** ourselves to work around bugs in the command-line parsing of MinGW.
352 ** It is possible (in theory) to only use this routine when compiling
353 ** with MinGW and to use built-in command-line parsing for MSVC and
@@ -456,11 +456,11 @@
456 }
457 argv[argc] = NULL;
458 *argcPtr = argc;
459 *((WCHAR ***)argvPtr) = argv;
460 }
461 #endif /* defined(_WIN32) */
462
463
464 /*
465 ** Convert all arguments from mbcs (or unicode) to UTF-8. Then
466 ** search g.argv for arguments "--args FILENAME". If found, then
@@ -480,17 +480,17 @@
480 int n; /* Number of bytes in one line */
481 char *z; /* General use string pointer */
482 char **newArgv; /* New expanded g.argv under construction */
483 char const * zFileName; /* input file name */
484 FILE * zInFile; /* input FILE */
485 #if defined(_WIN32)
486 WCHAR buf[MAX_PATH];
487 #endif
488
489 g.argc = argc;
490 g.argv = argv;
491 #if defined(_WIN32)
492 parse_windows_command_line(&g.argc, &g.argv);
493 GetModuleFileNameW(NULL, buf, MAX_PATH);
494 g.nameOfExe = fossil_unicode_to_utf8(buf);
495 for(i=0; i<g.argc; i++) g.argv[i] = fossil_unicode_to_utf8(g.argv[i]);
496 #else
497
+4 -4
--- src/main.c
+++ src/main.c
@@ -343,11 +343,11 @@
343343
if(g.db){
344344
db_close(0);
345345
}
346346
}
347347
348
-#if defined(_WIN32) && !defined(__MINGW32__)
348
+#if defined(_WIN32)
349349
/*
350350
** Parse the command-line arguments passed to windows. We do this
351351
** ourselves to work around bugs in the command-line parsing of MinGW.
352352
** It is possible (in theory) to only use this routine when compiling
353353
** with MinGW and to use built-in command-line parsing for MSVC and
@@ -456,11 +456,11 @@
456456
}
457457
argv[argc] = NULL;
458458
*argcPtr = argc;
459459
*((WCHAR ***)argvPtr) = argv;
460460
}
461
-#endif /* defined(_WIN32) && !defined(__MINGW32__) */
461
+#endif /* defined(_WIN32) */
462462
463463
464464
/*
465465
** Convert all arguments from mbcs (or unicode) to UTF-8. Then
466466
** search g.argv for arguments "--args FILENAME". If found, then
@@ -480,17 +480,17 @@
480480
int n; /* Number of bytes in one line */
481481
char *z; /* General use string pointer */
482482
char **newArgv; /* New expanded g.argv under construction */
483483
char const * zFileName; /* input file name */
484484
FILE * zInFile; /* input FILE */
485
-#if defined(_WIN32) && !defined(__MINGW32__)
485
+#if defined(_WIN32)
486486
WCHAR buf[MAX_PATH];
487487
#endif
488488
489489
g.argc = argc;
490490
g.argv = argv;
491
-#if defined(_WIN32) && !defined(__MINGW32__)
491
+#if defined(_WIN32)
492492
parse_windows_command_line(&g.argc, &g.argv);
493493
GetModuleFileNameW(NULL, buf, MAX_PATH);
494494
g.nameOfExe = fossil_unicode_to_utf8(buf);
495495
for(i=0; i<g.argc; i++) g.argv[i] = fossil_unicode_to_utf8(g.argv[i]);
496496
#else
497497
--- src/main.c
+++ src/main.c
@@ -343,11 +343,11 @@
343 if(g.db){
344 db_close(0);
345 }
346 }
347
348 #if defined(_WIN32) && !defined(__MINGW32__)
349 /*
350 ** Parse the command-line arguments passed to windows. We do this
351 ** ourselves to work around bugs in the command-line parsing of MinGW.
352 ** It is possible (in theory) to only use this routine when compiling
353 ** with MinGW and to use built-in command-line parsing for MSVC and
@@ -456,11 +456,11 @@
456 }
457 argv[argc] = NULL;
458 *argcPtr = argc;
459 *((WCHAR ***)argvPtr) = argv;
460 }
461 #endif /* defined(_WIN32) && !defined(__MINGW32__) */
462
463
464 /*
465 ** Convert all arguments from mbcs (or unicode) to UTF-8. Then
466 ** search g.argv for arguments "--args FILENAME". If found, then
@@ -480,17 +480,17 @@
480 int n; /* Number of bytes in one line */
481 char *z; /* General use string pointer */
482 char **newArgv; /* New expanded g.argv under construction */
483 char const * zFileName; /* input file name */
484 FILE * zInFile; /* input FILE */
485 #if defined(_WIN32) && !defined(__MINGW32__)
486 WCHAR buf[MAX_PATH];
487 #endif
488
489 g.argc = argc;
490 g.argv = argv;
491 #if defined(_WIN32) && !defined(__MINGW32__)
492 parse_windows_command_line(&g.argc, &g.argv);
493 GetModuleFileNameW(NULL, buf, MAX_PATH);
494 g.nameOfExe = fossil_unicode_to_utf8(buf);
495 for(i=0; i<g.argc; i++) g.argv[i] = fossil_unicode_to_utf8(g.argv[i]);
496 #else
497
--- src/main.c
+++ src/main.c
@@ -343,11 +343,11 @@
343 if(g.db){
344 db_close(0);
345 }
346 }
347
348 #if defined(_WIN32)
349 /*
350 ** Parse the command-line arguments passed to windows. We do this
351 ** ourselves to work around bugs in the command-line parsing of MinGW.
352 ** It is possible (in theory) to only use this routine when compiling
353 ** with MinGW and to use built-in command-line parsing for MSVC and
@@ -456,11 +456,11 @@
456 }
457 argv[argc] = NULL;
458 *argcPtr = argc;
459 *((WCHAR ***)argvPtr) = argv;
460 }
461 #endif /* defined(_WIN32) */
462
463
464 /*
465 ** Convert all arguments from mbcs (or unicode) to UTF-8. Then
466 ** search g.argv for arguments "--args FILENAME". If found, then
@@ -480,17 +480,17 @@
480 int n; /* Number of bytes in one line */
481 char *z; /* General use string pointer */
482 char **newArgv; /* New expanded g.argv under construction */
483 char const * zFileName; /* input file name */
484 FILE * zInFile; /* input FILE */
485 #if defined(_WIN32)
486 WCHAR buf[MAX_PATH];
487 #endif
488
489 g.argc = argc;
490 g.argv = argv;
491 #if defined(_WIN32)
492 parse_windows_command_line(&g.argc, &g.argv);
493 GetModuleFileNameW(NULL, buf, MAX_PATH);
494 g.nameOfExe = fossil_unicode_to_utf8(buf);
495 for(i=0; i<g.argc; i++) g.argv[i] = fossil_unicode_to_utf8(g.argv[i]);
496 #else
497
+1 -1
--- src/report.c
+++ src/report.c
@@ -730,11 +730,11 @@
730730
}
731731
if( zData[0] ){
732732
Blob content;
733733
@ </tr><tr style="background-color:%h(zBg)"><td colspan=%d(pState->nCol)>
734734
blob_init(&content, zData, -1);
735
- wiki_convert(&content, 0, 0);
735
+ wiki_convert(&content, 0, WIKI_NOBADLINKS);
736736
blob_reset(&content);
737737
}
738738
}else if( azName[i][0]=='#' ){
739739
zTid = zData;
740740
@ <td valign="top">%z(href("%R/tktview?name=%h",zData))%h(zData)</a></td>
741741
--- src/report.c
+++ src/report.c
@@ -730,11 +730,11 @@
730 }
731 if( zData[0] ){
732 Blob content;
733 @ </tr><tr style="background-color:%h(zBg)"><td colspan=%d(pState->nCol)>
734 blob_init(&content, zData, -1);
735 wiki_convert(&content, 0, 0);
736 blob_reset(&content);
737 }
738 }else if( azName[i][0]=='#' ){
739 zTid = zData;
740 @ <td valign="top">%z(href("%R/tktview?name=%h",zData))%h(zData)</a></td>
741
--- src/report.c
+++ src/report.c
@@ -730,11 +730,11 @@
730 }
731 if( zData[0] ){
732 Blob content;
733 @ </tr><tr style="background-color:%h(zBg)"><td colspan=%d(pState->nCol)>
734 blob_init(&content, zData, -1);
735 wiki_convert(&content, 0, WIKI_NOBADLINKS);
736 blob_reset(&content);
737 }
738 }else if( azName[i][0]=='#' ){
739 zTid = zData;
740 @ <td valign="top">%z(href("%R/tktview?name=%h",zData))%h(zData)</a></td>
741
+7 -1
--- src/setup.c
+++ src/setup.c
@@ -934,10 +934,17 @@
934934
@ if it does, your server will end up computing diffs and annotations for
935935
@ every historical version of every file and creating ZIPs and tarballs of
936936
@ every historical check-in, which can use a lot of CPU and bandwidth
937937
@ even for relatively small projects.</p>
938938
939
+ @ <hr />
940
+ onoff_attribute("Require a CAPTCHA if not logged in",
941
+ "require-captcha", "reqcapt", 1);
942
+ @ <p>Require a CAPTCHA for edit operations (appending, creating, or
943
+ @ editing wiki or tickets or adding attachments to wiki or tickets)
944
+ @ for users who are not logged in.</p>
945
+
939946
@ <hr />
940947
entry_attribute("Public pages", 30, "public-pages",
941948
"pubpage", "");
942949
@ <p>A comma-separated list of glob patterns for pages that are accessible
943950
@ without needing a login and using the privileges given by the
@@ -944,11 +951,10 @@
944951
@ "Default privileges" setting below. Example use case: Set this field
945952
@ to "/doc/trunk/www/*" to give anonymous users read-only permission to the
946953
@ latest version of the embedded documentation in the www/ folder without
947954
@ allowing them to see the rest of the source code.
948955
@ </p>
949
-
950956
951957
@ <hr />
952958
onoff_attribute("Allow users to register themselves",
953959
"self-register", "selfregister", 0);
954960
@ <p>Allow users to register themselves through the HTTP UI.
955961
--- src/setup.c
+++ src/setup.c
@@ -934,10 +934,17 @@
934 @ if it does, your server will end up computing diffs and annotations for
935 @ every historical version of every file and creating ZIPs and tarballs of
936 @ every historical check-in, which can use a lot of CPU and bandwidth
937 @ even for relatively small projects.</p>
938
 
 
 
 
 
 
 
939 @ <hr />
940 entry_attribute("Public pages", 30, "public-pages",
941 "pubpage", "");
942 @ <p>A comma-separated list of glob patterns for pages that are accessible
943 @ without needing a login and using the privileges given by the
@@ -944,11 +951,10 @@
944 @ "Default privileges" setting below. Example use case: Set this field
945 @ to "/doc/trunk/www/*" to give anonymous users read-only permission to the
946 @ latest version of the embedded documentation in the www/ folder without
947 @ allowing them to see the rest of the source code.
948 @ </p>
949
950
951 @ <hr />
952 onoff_attribute("Allow users to register themselves",
953 "self-register", "selfregister", 0);
954 @ <p>Allow users to register themselves through the HTTP UI.
955
--- src/setup.c
+++ src/setup.c
@@ -934,10 +934,17 @@
934 @ if it does, your server will end up computing diffs and annotations for
935 @ every historical version of every file and creating ZIPs and tarballs of
936 @ every historical check-in, which can use a lot of CPU and bandwidth
937 @ even for relatively small projects.</p>
938
939 @ <hr />
940 onoff_attribute("Require a CAPTCHA if not logged in",
941 "require-captcha", "reqcapt", 1);
942 @ <p>Require a CAPTCHA for edit operations (appending, creating, or
943 @ editing wiki or tickets or adding attachments to wiki or tickets)
944 @ for users who are not logged in.</p>
945
946 @ <hr />
947 entry_attribute("Public pages", 30, "public-pages",
948 "pubpage", "");
949 @ <p>A comma-separated list of glob patterns for pages that are accessible
950 @ without needing a login and using the privileges given by the
@@ -944,11 +951,10 @@
951 @ "Default privileges" setting below. Example use case: Set this field
952 @ to "/doc/trunk/www/*" to give anonymous users read-only permission to the
953 @ latest version of the embedded documentation in the www/ folder without
954 @ allowing them to see the rest of the source code.
955 @ </p>
 
956
957 @ <hr />
958 onoff_attribute("Allow users to register themselves",
959 "self-register", "selfregister", 0);
960 @ <p>Allow users to register themselves through the HTTP UI.
961
+31 -2
--- src/style.c
+++ src/style.c
@@ -47,16 +47,18 @@
4747
*/
4848
static int sideboxUsed = 0;
4949
5050
5151
/*
52
-** List of hyperlinks that need to be resolved by javascript in
52
+** List of hyperlinks and forms that need to be resolved by javascript in
5353
** the footer.
5454
*/
5555
char **aHref = 0;
5656
int nHref = 0;
5757
int nHrefAlloc = 0;
58
+char **aFormAction = 0;
59
+int nFormAction = 0;
5860
5961
/*
6062
** Generate and return a anchor tag like this:
6163
**
6264
** <a href="URL">
@@ -119,23 +121,49 @@
119121
aHref = fossil_realloc(aHref, nHrefAlloc*sizeof(aHref[0]));
120122
}
121123
aHref[nHref++] = zUrl;
122124
return mprintf("<a id=%d>", nHref);
123125
}
126
+
127
+/*
128
+** Generate <form method="post" action=ARG>. The ARG value is inserted
129
+** by javascript.
130
+*/
131
+void form_begin(const char *zOtherArgs, const char *zAction, ...){
132
+ char *zLink;
133
+ va_list ap;
134
+ if( zOtherArgs==0 ) zOtherArgs = "";
135
+ va_start(ap, zAction);
136
+ zLink = vmprintf(zAction, ap);
137
+ va_end(ap);
138
+ if( g.perm.Hyperlink && !g.javascriptHyperlink ){
139
+ @ <form method="POST" action="%z(zLink)" %s(zOtherArgs)>
140
+ }else{
141
+ int n;
142
+ aFormAction = fossil_realloc(aFormAction, (nFormAction+1)*sizeof(char*));
143
+ aFormAction[nFormAction++] = zLink;
144
+ n = nFormAction;
145
+ @ <form id="form%d(n)" method="POST" action='%R/login' %s(zOtherArgs)>
146
+ }
147
+}
124148
125149
/*
126150
** Generate javascript that will set the href= attribute on all anchors.
127151
*/
128152
void style_resolve_href(void){
129153
int i;
130
- if( !g.perm.Hyperlink || !g.javascriptHyperlink || nHref==0 ) return;
154
+ if( !g.perm.Hyperlink || !g.javascriptHyperlink ) return;
155
+ if( nHref==0 && nFormAction==0 ) return;
131156
@ <script type="text/JavaScript">
132157
@ /* <![CDATA[ */
133158
@ function u(i,h){gebi(i).href=h;}
134159
for(i=0; i<nHref; i++){
135160
@ u(%d(i+1),"%s(aHref[i])");
136161
}
162
+ for(i=0; i<nFormAction; i++){
163
+ @ gebi("form%d(i+1)").action="%s(aFormAction[i])";
164
+ }
137165
@ /* ]]> */
138166
@ </script>
139167
}
140168
141169
/*
@@ -684,10 +712,11 @@
684712
@ margin-top: 10px;
685713
},
686714
{ "div.captcha",
687715
"captcha display options",
688716
@ text-align: center;
717
+ @ padding: 1ex;
689718
},
690719
{ "table.captcha",
691720
"format for the layout table, used for the captcha display",
692721
@ margin: auto;
693722
@ padding: 10px;
694723
--- src/style.c
+++ src/style.c
@@ -47,16 +47,18 @@
47 */
48 static int sideboxUsed = 0;
49
50
51 /*
52 ** List of hyperlinks that need to be resolved by javascript in
53 ** the footer.
54 */
55 char **aHref = 0;
56 int nHref = 0;
57 int nHrefAlloc = 0;
 
 
58
59 /*
60 ** Generate and return a anchor tag like this:
61 **
62 ** <a href="URL">
@@ -119,23 +121,49 @@
119 aHref = fossil_realloc(aHref, nHrefAlloc*sizeof(aHref[0]));
120 }
121 aHref[nHref++] = zUrl;
122 return mprintf("<a id=%d>", nHref);
123 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
125 /*
126 ** Generate javascript that will set the href= attribute on all anchors.
127 */
128 void style_resolve_href(void){
129 int i;
130 if( !g.perm.Hyperlink || !g.javascriptHyperlink || nHref==0 ) return;
 
131 @ <script type="text/JavaScript">
132 @ /* <![CDATA[ */
133 @ function u(i,h){gebi(i).href=h;}
134 for(i=0; i<nHref; i++){
135 @ u(%d(i+1),"%s(aHref[i])");
136 }
 
 
 
137 @ /* ]]> */
138 @ </script>
139 }
140
141 /*
@@ -684,10 +712,11 @@
684 @ margin-top: 10px;
685 },
686 { "div.captcha",
687 "captcha display options",
688 @ text-align: center;
 
689 },
690 { "table.captcha",
691 "format for the layout table, used for the captcha display",
692 @ margin: auto;
693 @ padding: 10px;
694
--- src/style.c
+++ src/style.c
@@ -47,16 +47,18 @@
47 */
48 static int sideboxUsed = 0;
49
50
51 /*
52 ** List of hyperlinks and forms that need to be resolved by javascript in
53 ** the footer.
54 */
55 char **aHref = 0;
56 int nHref = 0;
57 int nHrefAlloc = 0;
58 char **aFormAction = 0;
59 int nFormAction = 0;
60
61 /*
62 ** Generate and return a anchor tag like this:
63 **
64 ** <a href="URL">
@@ -119,23 +121,49 @@
121 aHref = fossil_realloc(aHref, nHrefAlloc*sizeof(aHref[0]));
122 }
123 aHref[nHref++] = zUrl;
124 return mprintf("<a id=%d>", nHref);
125 }
126
127 /*
128 ** Generate <form method="post" action=ARG>. The ARG value is inserted
129 ** by javascript.
130 */
131 void form_begin(const char *zOtherArgs, const char *zAction, ...){
132 char *zLink;
133 va_list ap;
134 if( zOtherArgs==0 ) zOtherArgs = "";
135 va_start(ap, zAction);
136 zLink = vmprintf(zAction, ap);
137 va_end(ap);
138 if( g.perm.Hyperlink && !g.javascriptHyperlink ){
139 @ <form method="POST" action="%z(zLink)" %s(zOtherArgs)>
140 }else{
141 int n;
142 aFormAction = fossil_realloc(aFormAction, (nFormAction+1)*sizeof(char*));
143 aFormAction[nFormAction++] = zLink;
144 n = nFormAction;
145 @ <form id="form%d(n)" method="POST" action='%R/login' %s(zOtherArgs)>
146 }
147 }
148
149 /*
150 ** Generate javascript that will set the href= attribute on all anchors.
151 */
152 void style_resolve_href(void){
153 int i;
154 if( !g.perm.Hyperlink || !g.javascriptHyperlink ) return;
155 if( nHref==0 && nFormAction==0 ) return;
156 @ <script type="text/JavaScript">
157 @ /* <![CDATA[ */
158 @ function u(i,h){gebi(i).href=h;}
159 for(i=0; i<nHref; i++){
160 @ u(%d(i+1),"%s(aHref[i])");
161 }
162 for(i=0; i<nFormAction; i++){
163 @ gebi("form%d(i+1)").action="%s(aFormAction[i])";
164 }
165 @ /* ]]> */
166 @ </script>
167 }
168
169 /*
@@ -684,10 +712,11 @@
712 @ margin-top: 10px;
713 },
714 { "div.captcha",
715 "captcha display options",
716 @ text-align: center;
717 @ padding: 1ex;
718 },
719 { "table.captcha",
720 "format for the layout table, used for the captcha display",
721 @ margin: auto;
722 @ padding: 10px;
723
+34 -32
--- src/sync.c
+++ src/sync.c
@@ -19,19 +19,10 @@
1919
*/
2020
#include "config.h"
2121
#include "sync.h"
2222
#include <assert.h>
2323
24
-#if INTERFACE
25
-/*
26
-** Flags used to determine which direction(s) an autosync goes in.
27
-*/
28
-#define AUTOSYNC_PUSH 1
29
-#define AUTOSYNC_PULL 2
30
-
31
-#endif /* INTERFACE */
32
-
3324
/*
3425
** If the repository is configured for autosyncing, then do an
3526
** autosync. This will be a pull if the argument is true or a push
3627
** if the argument is false.
3728
**
@@ -44,16 +35,16 @@
4435
int rc;
4536
int configSync = 0; /* configuration changes transferred */
4637
if( g.fNoSync ){
4738
return 0;
4839
}
49
- if( flags==AUTOSYNC_PUSH && db_get_boolean("dont-push",0) ){
40
+ if( flags==SYNC_PUSH && db_get_boolean("dont-push",0) ){
5041
return 0;
5142
}
5243
zAutosync = db_get("autosync", 0);
5344
if( zAutosync ){
54
- if( (flags & AUTOSYNC_PUSH)!=0 && memcmp(zAutosync,"pull",4)==0 ){
45
+ if( (flags & SYNC_PUSH)!=0 && memcmp(zAutosync,"pull",4)==0 ){
5546
return 0; /* Do not auto-push when autosync=pullonly */
5647
}
5748
if( is_false(zAutosync) ){
5849
return 0; /* Autosync is completely off */
5950
}
@@ -79,13 +70,14 @@
7970
** autosync, or something?
8071
*/
8172
configSync = CONFIGSET_SHUN;
8273
}
8374
#endif
75
+ if( find_option("verbose","v",0)!=0 ) flags |= SYNC_VERBOSE;
8476
fossil_print("Autosync: %s\n", g.urlCanonical);
8577
url_enable_proxy("via proxy: ");
86
- rc = client_sync((flags & AUTOSYNC_PUSH)!=0, 1, 0, 0, configSync, 0);
78
+ rc = client_sync(flags, configSync, 0);
8779
if( rc ) fossil_warning("Autosync failed");
8880
return rc;
8981
}
9082
9183
/*
@@ -92,17 +84,22 @@
9284
** This routine processes the command-line argument for push, pull,
9385
** and sync. If a command-line argument is given, that is the URL
9486
** of a server to sync against. If no argument is given, use the
9587
** most recently synced URL. Remember the current URL for next time.
9688
*/
97
-static void process_sync_args(int *pConfigSync, int *pPrivate){
89
+static void process_sync_args(unsigned *pConfigFlags, unsigned *pSyncFlags){
9890
const char *zUrl = 0;
9991
const char *zPw = 0;
100
- int configSync = 0;
92
+ unsigned configSync = 0;
10193
int urlOptional = find_option("autourl",0,0)!=0;
10294
g.dontKeepUrl = find_option("once",0,0)!=0;
103
- *pPrivate = find_option("private",0,0)!=0;
95
+ if( find_option("private",0,0)!=0 ){
96
+ *pSyncFlags |= SYNC_PRIVATE;
97
+ }
98
+ if( find_option("verbose","v",0)!=0 ){
99
+ *pSyncFlags |= SYNC_VERBOSE;
100
+ }
104101
url_proxy_options();
105102
db_find_and_open_repository(0, 0);
106103
db_open_config(0);
107104
if( g.argc==2 ){
108105
zUrl = db_get("last-sync-url", 0);
@@ -127,14 +124,20 @@
127124
db_set("last-sync-url", g.urlCanonical, 0);
128125
if( g.urlPasswd ) db_set("last-sync-pw", obscure(g.urlPasswd), 0);
129126
}
130127
user_select();
131128
if( g.argc==2 ){
132
- fossil_print("Server: %s\n", g.urlCanonical);
129
+ if( ((*pSyncFlags) & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL) ){
130
+ fossil_print("Sync with %s\n", g.urlCanonical);
131
+ }else if( (*pSyncFlags) & SYNC_PUSH ){
132
+ fossil_print("Push to %s\n", g.urlCanonical);
133
+ }else if( (*pSyncFlags) & SYNC_PULL ){
134
+ fossil_print("Pull from %s\n", g.urlCanonical);
135
+ }
133136
}
134137
url_enable_proxy("via proxy: ");
135
- *pConfigSync = configSync;
138
+ *pConfigFlags |= configSync;
136139
}
137140
138141
/*
139142
** COMMAND: pull
140143
**
@@ -156,14 +159,14 @@
156159
** remote repository.
157160
**
158161
** See also: clone, push, sync, remote-url
159162
*/
160163
void pull_cmd(void){
161
- int syncFlags;
162
- int bPrivate;
163
- process_sync_args(&syncFlags, &bPrivate);
164
- client_sync(0,1,0,bPrivate,syncFlags,0);
164
+ unsigned configFlags = 0;
165
+ unsigned syncFlags = SYNC_PULL;
166
+ process_sync_args(&configFlags, &syncFlags);
167
+ client_sync(syncFlags, configFlags, 0);
165168
}
166169
167170
/*
168171
** COMMAND: push
169172
**
@@ -185,17 +188,17 @@
185188
** remote repository.
186189
**
187190
** See also: clone, pull, sync, remote-url
188191
*/
189192
void push_cmd(void){
190
- int syncFlags;
191
- int bPrivate;
192
- process_sync_args(&syncFlags, &bPrivate);
193
+ unsigned configFlags = 0;
194
+ unsigned syncFlags = SYNC_PUSH;
195
+ process_sync_args(&configFlags, &syncFlags);
193196
if( db_get_boolean("dont-push",0) ){
194197
fossil_fatal("pushing is prohibited: the 'dont-push' option is set");
195198
}
196
- client_sync(1,0,0,bPrivate,0,0);
199
+ client_sync(syncFlags, 0, 0);
197200
}
198201
199202
200203
/*
201204
** COMMAND: sync
@@ -223,17 +226,16 @@
223226
** remote repository.
224227
**
225228
** See also: clone, push, pull, remote-url
226229
*/
227230
void sync_cmd(void){
228
- int syncFlags;
229
- int bPrivate;
230
- int pushFlag = 1;
231
- process_sync_args(&syncFlags, &bPrivate);
232
- if( db_get_boolean("dont-push",0) ) pushFlag = 0;
233
- client_sync(pushFlag,1,0,bPrivate,syncFlags,0);
234
- if( pushFlag==0 ){
231
+ unsigned configFlags = 0;
232
+ unsigned syncFlags = SYNC_PUSH|SYNC_PULL;
233
+ process_sync_args(&configFlags, &syncFlags);
234
+ if( db_get_boolean("dont-push",0) ) syncFlags &= ~SYNC_PUSH;
235
+ client_sync(syncFlags, configFlags, 0);
236
+ if( (syncFlags & SYNC_PUSH)==0 ){
235237
fossil_warning("pull only: the 'dont-push' option is set");
236238
}
237239
}
238240
239241
/*
240242
--- src/sync.c
+++ src/sync.c
@@ -19,19 +19,10 @@
19 */
20 #include "config.h"
21 #include "sync.h"
22 #include <assert.h>
23
24 #if INTERFACE
25 /*
26 ** Flags used to determine which direction(s) an autosync goes in.
27 */
28 #define AUTOSYNC_PUSH 1
29 #define AUTOSYNC_PULL 2
30
31 #endif /* INTERFACE */
32
33 /*
34 ** If the repository is configured for autosyncing, then do an
35 ** autosync. This will be a pull if the argument is true or a push
36 ** if the argument is false.
37 **
@@ -44,16 +35,16 @@
44 int rc;
45 int configSync = 0; /* configuration changes transferred */
46 if( g.fNoSync ){
47 return 0;
48 }
49 if( flags==AUTOSYNC_PUSH && db_get_boolean("dont-push",0) ){
50 return 0;
51 }
52 zAutosync = db_get("autosync", 0);
53 if( zAutosync ){
54 if( (flags & AUTOSYNC_PUSH)!=0 && memcmp(zAutosync,"pull",4)==0 ){
55 return 0; /* Do not auto-push when autosync=pullonly */
56 }
57 if( is_false(zAutosync) ){
58 return 0; /* Autosync is completely off */
59 }
@@ -79,13 +70,14 @@
79 ** autosync, or something?
80 */
81 configSync = CONFIGSET_SHUN;
82 }
83 #endif
 
84 fossil_print("Autosync: %s\n", g.urlCanonical);
85 url_enable_proxy("via proxy: ");
86 rc = client_sync((flags & AUTOSYNC_PUSH)!=0, 1, 0, 0, configSync, 0);
87 if( rc ) fossil_warning("Autosync failed");
88 return rc;
89 }
90
91 /*
@@ -92,17 +84,22 @@
92 ** This routine processes the command-line argument for push, pull,
93 ** and sync. If a command-line argument is given, that is the URL
94 ** of a server to sync against. If no argument is given, use the
95 ** most recently synced URL. Remember the current URL for next time.
96 */
97 static void process_sync_args(int *pConfigSync, int *pPrivate){
98 const char *zUrl = 0;
99 const char *zPw = 0;
100 int configSync = 0;
101 int urlOptional = find_option("autourl",0,0)!=0;
102 g.dontKeepUrl = find_option("once",0,0)!=0;
103 *pPrivate = find_option("private",0,0)!=0;
 
 
 
 
 
104 url_proxy_options();
105 db_find_and_open_repository(0, 0);
106 db_open_config(0);
107 if( g.argc==2 ){
108 zUrl = db_get("last-sync-url", 0);
@@ -127,14 +124,20 @@
127 db_set("last-sync-url", g.urlCanonical, 0);
128 if( g.urlPasswd ) db_set("last-sync-pw", obscure(g.urlPasswd), 0);
129 }
130 user_select();
131 if( g.argc==2 ){
132 fossil_print("Server: %s\n", g.urlCanonical);
 
 
 
 
 
 
133 }
134 url_enable_proxy("via proxy: ");
135 *pConfigSync = configSync;
136 }
137
138 /*
139 ** COMMAND: pull
140 **
@@ -156,14 +159,14 @@
156 ** remote repository.
157 **
158 ** See also: clone, push, sync, remote-url
159 */
160 void pull_cmd(void){
161 int syncFlags;
162 int bPrivate;
163 process_sync_args(&syncFlags, &bPrivate);
164 client_sync(0,1,0,bPrivate,syncFlags,0);
165 }
166
167 /*
168 ** COMMAND: push
169 **
@@ -185,17 +188,17 @@
185 ** remote repository.
186 **
187 ** See also: clone, pull, sync, remote-url
188 */
189 void push_cmd(void){
190 int syncFlags;
191 int bPrivate;
192 process_sync_args(&syncFlags, &bPrivate);
193 if( db_get_boolean("dont-push",0) ){
194 fossil_fatal("pushing is prohibited: the 'dont-push' option is set");
195 }
196 client_sync(1,0,0,bPrivate,0,0);
197 }
198
199
200 /*
201 ** COMMAND: sync
@@ -223,17 +226,16 @@
223 ** remote repository.
224 **
225 ** See also: clone, push, pull, remote-url
226 */
227 void sync_cmd(void){
228 int syncFlags;
229 int bPrivate;
230 int pushFlag = 1;
231 process_sync_args(&syncFlags, &bPrivate);
232 if( db_get_boolean("dont-push",0) ) pushFlag = 0;
233 client_sync(pushFlag,1,0,bPrivate,syncFlags,0);
234 if( pushFlag==0 ){
235 fossil_warning("pull only: the 'dont-push' option is set");
236 }
237 }
238
239 /*
240
--- src/sync.c
+++ src/sync.c
@@ -19,19 +19,10 @@
19 */
20 #include "config.h"
21 #include "sync.h"
22 #include <assert.h>
23
 
 
 
 
 
 
 
 
 
24 /*
25 ** If the repository is configured for autosyncing, then do an
26 ** autosync. This will be a pull if the argument is true or a push
27 ** if the argument is false.
28 **
@@ -44,16 +35,16 @@
35 int rc;
36 int configSync = 0; /* configuration changes transferred */
37 if( g.fNoSync ){
38 return 0;
39 }
40 if( flags==SYNC_PUSH && db_get_boolean("dont-push",0) ){
41 return 0;
42 }
43 zAutosync = db_get("autosync", 0);
44 if( zAutosync ){
45 if( (flags & SYNC_PUSH)!=0 && memcmp(zAutosync,"pull",4)==0 ){
46 return 0; /* Do not auto-push when autosync=pullonly */
47 }
48 if( is_false(zAutosync) ){
49 return 0; /* Autosync is completely off */
50 }
@@ -79,13 +70,14 @@
70 ** autosync, or something?
71 */
72 configSync = CONFIGSET_SHUN;
73 }
74 #endif
75 if( find_option("verbose","v",0)!=0 ) flags |= SYNC_VERBOSE;
76 fossil_print("Autosync: %s\n", g.urlCanonical);
77 url_enable_proxy("via proxy: ");
78 rc = client_sync(flags, configSync, 0);
79 if( rc ) fossil_warning("Autosync failed");
80 return rc;
81 }
82
83 /*
@@ -92,17 +84,22 @@
84 ** This routine processes the command-line argument for push, pull,
85 ** and sync. If a command-line argument is given, that is the URL
86 ** of a server to sync against. If no argument is given, use the
87 ** most recently synced URL. Remember the current URL for next time.
88 */
89 static void process_sync_args(unsigned *pConfigFlags, unsigned *pSyncFlags){
90 const char *zUrl = 0;
91 const char *zPw = 0;
92 unsigned configSync = 0;
93 int urlOptional = find_option("autourl",0,0)!=0;
94 g.dontKeepUrl = find_option("once",0,0)!=0;
95 if( find_option("private",0,0)!=0 ){
96 *pSyncFlags |= SYNC_PRIVATE;
97 }
98 if( find_option("verbose","v",0)!=0 ){
99 *pSyncFlags |= SYNC_VERBOSE;
100 }
101 url_proxy_options();
102 db_find_and_open_repository(0, 0);
103 db_open_config(0);
104 if( g.argc==2 ){
105 zUrl = db_get("last-sync-url", 0);
@@ -127,14 +124,20 @@
124 db_set("last-sync-url", g.urlCanonical, 0);
125 if( g.urlPasswd ) db_set("last-sync-pw", obscure(g.urlPasswd), 0);
126 }
127 user_select();
128 if( g.argc==2 ){
129 if( ((*pSyncFlags) & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL) ){
130 fossil_print("Sync with %s\n", g.urlCanonical);
131 }else if( (*pSyncFlags) & SYNC_PUSH ){
132 fossil_print("Push to %s\n", g.urlCanonical);
133 }else if( (*pSyncFlags) & SYNC_PULL ){
134 fossil_print("Pull from %s\n", g.urlCanonical);
135 }
136 }
137 url_enable_proxy("via proxy: ");
138 *pConfigFlags |= configSync;
139 }
140
141 /*
142 ** COMMAND: pull
143 **
@@ -156,14 +159,14 @@
159 ** remote repository.
160 **
161 ** See also: clone, push, sync, remote-url
162 */
163 void pull_cmd(void){
164 unsigned configFlags = 0;
165 unsigned syncFlags = SYNC_PULL;
166 process_sync_args(&configFlags, &syncFlags);
167 client_sync(syncFlags, configFlags, 0);
168 }
169
170 /*
171 ** COMMAND: push
172 **
@@ -185,17 +188,17 @@
188 ** remote repository.
189 **
190 ** See also: clone, pull, sync, remote-url
191 */
192 void push_cmd(void){
193 unsigned configFlags = 0;
194 unsigned syncFlags = SYNC_PUSH;
195 process_sync_args(&configFlags, &syncFlags);
196 if( db_get_boolean("dont-push",0) ){
197 fossil_fatal("pushing is prohibited: the 'dont-push' option is set");
198 }
199 client_sync(syncFlags, 0, 0);
200 }
201
202
203 /*
204 ** COMMAND: sync
@@ -223,17 +226,16 @@
226 ** remote repository.
227 **
228 ** See also: clone, push, pull, remote-url
229 */
230 void sync_cmd(void){
231 unsigned configFlags = 0;
232 unsigned syncFlags = SYNC_PUSH|SYNC_PULL;
233 process_sync_args(&configFlags, &syncFlags);
234 if( db_get_boolean("dont-push",0) ) syncFlags &= ~SYNC_PUSH;
235 client_sync(syncFlags, configFlags, 0);
236 if( (syncFlags & SYNC_PUSH)==0 ){
 
237 fossil_warning("pull only: the 'dont-push' option is set");
238 }
239 }
240
241 /*
242
+50 -10
--- src/th_main.c
+++ src/th_main.c
@@ -98,11 +98,12 @@
9898
return zRc;
9999
}
100100
101101
/*
102102
** Send text to the appropriate output: Either to the console
103
-** or to the CGI reply buffer.
103
+** or to the CGI reply buffer. Escape all characters with special
104
+** meaning to HTML if the encode parameter is true.
104105
*/
105106
static void sendText(const char *z, int n, int encode){
106107
if( enableOutput && n ){
107108
if( n<0 ) n = strlen(z);
108109
if( encode ){
@@ -130,11 +131,11 @@
130131
131132
/*
132133
** TH command: puts STRING
133134
** TH command: html STRING
134135
**
135
-** Output STRING as HTML (html) or unchanged (puts).
136
+** Output STRING escaped for HTML (html) or unchanged (puts).
136137
*/
137138
static int putsCmd(
138139
Th_Interp *interp,
139140
void *pConvert,
140141
int argc,
@@ -142,11 +143,11 @@
142143
int *argl
143144
){
144145
if( argc!=2 ){
145146
return Th_WrongNumArgs(interp, "puts STRING");
146147
}
147
- sendText((char*)argv[1], argl[1], pConvert!=0);
148
+ sendText((char*)argv[1], argl[1], *(unsigned int*)pConvert);
148149
return TH_OK;
149150
}
150151
151152
/*
152153
** TH command: wiki STRING
@@ -158,17 +159,18 @@
158159
void *p,
159160
int argc,
160161
const char **argv,
161162
int *argl
162163
){
164
+ int flags = WIKI_INLINE | WIKI_NOBADLINKS | *(unsigned int*)p;
163165
if( argc!=2 ){
164166
return Th_WrongNumArgs(interp, "wiki STRING");
165167
}
166168
if( enableOutput ){
167169
Blob src;
168170
blob_init(&src, (char*)argv[1], argl[1]);
169
- wiki_convert(&src, 0, WIKI_INLINE);
171
+ wiki_convert(&src, 0, flags);
170172
blob_reset(&src);
171173
}
172174
return TH_OK;
173175
}
174176
@@ -530,38 +532,76 @@
530532
sqlite3_snprintf(sizeof(zUTime), zUTime, "%llu", x);
531533
Th_SetResult(interp, zUTime, -1);
532534
return TH_OK;
533535
}
534536
537
+
538
+/*
539
+** TH1 command: randhex N
540
+**
541
+** Return N*2 random hexadecimal digits with N<50. If N is omitted,
542
+** use a value of 10.
543
+*/
544
+static int randhexCmd(
545
+ Th_Interp *interp,
546
+ void *p,
547
+ int argc,
548
+ const char **argv,
549
+ int *argl
550
+){
551
+ int n;
552
+ unsigned char aRand[50];
553
+ unsigned char zOut[100];
554
+ if( argc!=1 && argc!=2 ){
555
+ return Th_WrongNumArgs(interp, "repository ?BOOLEAN?");
556
+ }
557
+ if( argc==2 ){
558
+ if( Th_ToInt(interp, argv[1], argl[1], &n) ){
559
+ return TH_ERROR;
560
+ }
561
+ if( n<1 ) n = 1;
562
+ if( n>sizeof(aRand) ) n = sizeof(aRand);
563
+ }else{
564
+ n = 10;
565
+ }
566
+ sqlite3_randomness(n, aRand);
567
+ encode16(aRand, zOut, n);
568
+ Th_SetResult(interp, (const char *)zOut, -1);
569
+ return TH_OK;
570
+}
571
+
535572
536573
/*
537574
** Make sure the interpreter has been initialized. Initialize it if
538575
** it has not been already.
539576
**
540577
** The interpreter is stored in the g.interp global variable.
541578
*/
542579
void Th_FossilInit(int needConfig, int forceSetup){
543580
int wasInit = 0;
581
+ static unsigned int aFlags[] = { 0, 1, WIKI_LINKSONLY };
544582
static struct _Command {
545583
const char *zName;
546584
Th_CommandProc xProc;
547585
void *pContext;
548586
} aCommand[] = {
549587
{"anycap", anycapCmd, 0},
550588
{"combobox", comboboxCmd, 0},
589
+ {"date", dateCmd, 0},
590
+ {"decorate", wikiCmd, (void*)&aFlags[2]},
551591
{"enable_output", enableOutputCmd, 0},
552
- {"linecount", linecntCmd, 0},
553592
{"hascap", hascapCmd, 0},
554593
{"hasfeature", hasfeatureCmd, 0},
594
+ {"html", putsCmd, (void*)&aFlags[0]},
555595
{"htmlize", htmlizeCmd, 0},
556
- {"date", dateCmd, 0},
557
- {"html", putsCmd, 0},
558
- {"puts", putsCmd, (void*)1},
559
- {"wiki", wikiCmd, 0},
596
+ {"linecount", linecntCmd, 0},
597
+ {"puts", putsCmd, (void*)&aFlags[1]},
598
+ {"randhex", randhexCmd, 0},
560599
{"repository", repositoryCmd, 0},
561
- {"utime", utimeCmd, 0},
562600
{"stime", stimeCmd, 0},
601
+ {"utime", utimeCmd, 0},
602
+ {"wiki", wikiCmd, (void*)&aFlags[0]},
563603
{0, 0, 0}
564604
};
565605
if( needConfig ){
566606
/*
567607
** This function uses several settings which may be defined in the
568608
--- src/th_main.c
+++ src/th_main.c
@@ -98,11 +98,12 @@
98 return zRc;
99 }
100
101 /*
102 ** Send text to the appropriate output: Either to the console
103 ** or to the CGI reply buffer.
 
104 */
105 static void sendText(const char *z, int n, int encode){
106 if( enableOutput && n ){
107 if( n<0 ) n = strlen(z);
108 if( encode ){
@@ -130,11 +131,11 @@
130
131 /*
132 ** TH command: puts STRING
133 ** TH command: html STRING
134 **
135 ** Output STRING as HTML (html) or unchanged (puts).
136 */
137 static int putsCmd(
138 Th_Interp *interp,
139 void *pConvert,
140 int argc,
@@ -142,11 +143,11 @@
142 int *argl
143 ){
144 if( argc!=2 ){
145 return Th_WrongNumArgs(interp, "puts STRING");
146 }
147 sendText((char*)argv[1], argl[1], pConvert!=0);
148 return TH_OK;
149 }
150
151 /*
152 ** TH command: wiki STRING
@@ -158,17 +159,18 @@
158 void *p,
159 int argc,
160 const char **argv,
161 int *argl
162 ){
 
163 if( argc!=2 ){
164 return Th_WrongNumArgs(interp, "wiki STRING");
165 }
166 if( enableOutput ){
167 Blob src;
168 blob_init(&src, (char*)argv[1], argl[1]);
169 wiki_convert(&src, 0, WIKI_INLINE);
170 blob_reset(&src);
171 }
172 return TH_OK;
173 }
174
@@ -530,38 +532,76 @@
530 sqlite3_snprintf(sizeof(zUTime), zUTime, "%llu", x);
531 Th_SetResult(interp, zUTime, -1);
532 return TH_OK;
533 }
534
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
535
536 /*
537 ** Make sure the interpreter has been initialized. Initialize it if
538 ** it has not been already.
539 **
540 ** The interpreter is stored in the g.interp global variable.
541 */
542 void Th_FossilInit(int needConfig, int forceSetup){
543 int wasInit = 0;
 
544 static struct _Command {
545 const char *zName;
546 Th_CommandProc xProc;
547 void *pContext;
548 } aCommand[] = {
549 {"anycap", anycapCmd, 0},
550 {"combobox", comboboxCmd, 0},
 
 
551 {"enable_output", enableOutputCmd, 0},
552 {"linecount", linecntCmd, 0},
553 {"hascap", hascapCmd, 0},
554 {"hasfeature", hasfeatureCmd, 0},
 
555 {"htmlize", htmlizeCmd, 0},
556 {"date", dateCmd, 0},
557 {"html", putsCmd, 0},
558 {"puts", putsCmd, (void*)1},
559 {"wiki", wikiCmd, 0},
560 {"repository", repositoryCmd, 0},
561 {"utime", utimeCmd, 0},
562 {"stime", stimeCmd, 0},
 
 
563 {0, 0, 0}
564 };
565 if( needConfig ){
566 /*
567 ** This function uses several settings which may be defined in the
568
--- src/th_main.c
+++ src/th_main.c
@@ -98,11 +98,12 @@
98 return zRc;
99 }
100
101 /*
102 ** Send text to the appropriate output: Either to the console
103 ** or to the CGI reply buffer. Escape all characters with special
104 ** meaning to HTML if the encode parameter is true.
105 */
106 static void sendText(const char *z, int n, int encode){
107 if( enableOutput && n ){
108 if( n<0 ) n = strlen(z);
109 if( encode ){
@@ -130,11 +131,11 @@
131
132 /*
133 ** TH command: puts STRING
134 ** TH command: html STRING
135 **
136 ** Output STRING escaped for HTML (html) or unchanged (puts).
137 */
138 static int putsCmd(
139 Th_Interp *interp,
140 void *pConvert,
141 int argc,
@@ -142,11 +143,11 @@
143 int *argl
144 ){
145 if( argc!=2 ){
146 return Th_WrongNumArgs(interp, "puts STRING");
147 }
148 sendText((char*)argv[1], argl[1], *(unsigned int*)pConvert);
149 return TH_OK;
150 }
151
152 /*
153 ** TH command: wiki STRING
@@ -158,17 +159,18 @@
159 void *p,
160 int argc,
161 const char **argv,
162 int *argl
163 ){
164 int flags = WIKI_INLINE | WIKI_NOBADLINKS | *(unsigned int*)p;
165 if( argc!=2 ){
166 return Th_WrongNumArgs(interp, "wiki STRING");
167 }
168 if( enableOutput ){
169 Blob src;
170 blob_init(&src, (char*)argv[1], argl[1]);
171 wiki_convert(&src, 0, flags);
172 blob_reset(&src);
173 }
174 return TH_OK;
175 }
176
@@ -530,38 +532,76 @@
532 sqlite3_snprintf(sizeof(zUTime), zUTime, "%llu", x);
533 Th_SetResult(interp, zUTime, -1);
534 return TH_OK;
535 }
536
537
538 /*
539 ** TH1 command: randhex N
540 **
541 ** Return N*2 random hexadecimal digits with N<50. If N is omitted,
542 ** use a value of 10.
543 */
544 static int randhexCmd(
545 Th_Interp *interp,
546 void *p,
547 int argc,
548 const char **argv,
549 int *argl
550 ){
551 int n;
552 unsigned char aRand[50];
553 unsigned char zOut[100];
554 if( argc!=1 && argc!=2 ){
555 return Th_WrongNumArgs(interp, "repository ?BOOLEAN?");
556 }
557 if( argc==2 ){
558 if( Th_ToInt(interp, argv[1], argl[1], &n) ){
559 return TH_ERROR;
560 }
561 if( n<1 ) n = 1;
562 if( n>sizeof(aRand) ) n = sizeof(aRand);
563 }else{
564 n = 10;
565 }
566 sqlite3_randomness(n, aRand);
567 encode16(aRand, zOut, n);
568 Th_SetResult(interp, (const char *)zOut, -1);
569 return TH_OK;
570 }
571
572
573 /*
574 ** Make sure the interpreter has been initialized. Initialize it if
575 ** it has not been already.
576 **
577 ** The interpreter is stored in the g.interp global variable.
578 */
579 void Th_FossilInit(int needConfig, int forceSetup){
580 int wasInit = 0;
581 static unsigned int aFlags[] = { 0, 1, WIKI_LINKSONLY };
582 static struct _Command {
583 const char *zName;
584 Th_CommandProc xProc;
585 void *pContext;
586 } aCommand[] = {
587 {"anycap", anycapCmd, 0},
588 {"combobox", comboboxCmd, 0},
589 {"date", dateCmd, 0},
590 {"decorate", wikiCmd, (void*)&aFlags[2]},
591 {"enable_output", enableOutputCmd, 0},
 
592 {"hascap", hascapCmd, 0},
593 {"hasfeature", hasfeatureCmd, 0},
594 {"html", putsCmd, (void*)&aFlags[0]},
595 {"htmlize", htmlizeCmd, 0},
596 {"linecount", linecntCmd, 0},
597 {"puts", putsCmd, (void*)&aFlags[1]},
598 {"randhex", randhexCmd, 0},
 
599 {"repository", repositoryCmd, 0},
 
600 {"stime", stimeCmd, 0},
601 {"utime", utimeCmd, 0},
602 {"wiki", wikiCmd, (void*)&aFlags[0]},
603 {0, 0, 0}
604 };
605 if( needConfig ){
606 /*
607 ** This function uses several settings which may be defined in the
608
+8 -6
--- src/timeline.c
+++ src/timeline.c
@@ -85,10 +85,11 @@
8585
** Generate a hyperlink to a user. This will link to a timeline showing
8686
** events by that user. If the date+time is specified, then the timeline
8787
** is centered on that date+time.
8888
*/
8989
void hyperlink_to_user(const char *zU, const char *zD, const char *zSuf){
90
+ if( zU==0 || zU[0]==0 ) zU = "anonymous";
9091
if( zSuf==0 ) zSuf = "";
9192
if( g.perm.Hyperlink ){
9293
if( zD && zD[0] ){
9394
@ %z(href("%R/timeline?c=%T&u=%T",zD,zU))%h(zU)</a>%s(zSuf)
9495
}else{
@@ -209,13 +210,13 @@
209210
210211
zPrevDate[0] = 0;
211212
plainText = db_get_int("timeline-plaintext", 0);
212213
mxWikiLen = db_get_int("timeline-max-comment", 0);
213214
if( db_get_boolean("timeline-block-markup", 0) ){
214
- wikiFlags = WIKI_INLINE;
215
+ wikiFlags = WIKI_INLINE | WIKI_NOBADLINKS;
215216
}else{
216
- wikiFlags = WIKI_INLINE | WIKI_NOBLOCK;
217
+ wikiFlags = WIKI_INLINE | WIKI_NOBLOCK | WIKI_NOBADLINKS;
217218
}
218219
if( tmFlags & TIMELINE_GRAPH ){
219220
pGraph = graph_init();
220221
/* style is not moved to css, because this is
221222
** a technical div for the timeline graph
@@ -237,10 +238,11 @@
237238
const char *zDate = db_column_text(pQuery, 2);
238239
const char *zType = db_column_text(pQuery, 7);
239240
const char *zUser = db_column_text(pQuery, 4);
240241
const char *zTagList = db_column_text(pQuery, 8);
241242
int tagid = db_column_int(pQuery, 9);
243
+ const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
242244
const char *zBr = 0; /* Branch */
243245
int commentColumn = 3; /* Column containing comment text */
244246
int modPending; /* Pending moderation */
245247
char zTime[8];
246248
@@ -372,15 +374,15 @@
372374
373375
/* Generate the "user: USERNAME" at the end of the comment, together
374376
** with a hyperlink to another timeline for that user.
375377
*/
376378
if( zTagList && zTagList[0]==0 ) zTagList = 0;
377
- if( g.perm.Hyperlink && fossil_strcmp(zUser, zThisUser)!=0 ){
378
- char *zLink = mprintf("%R/timeline?u=%h&c=%t&nd", zUser, zDate);
379
- @ (user: %z(href("%z",zLink))%h(zUser)</a>%s(zTagList?",":"\051")
379
+ if( g.perm.Hyperlink && fossil_strcmp(zDispUser, zThisUser)!=0 ){
380
+ char *zLink = mprintf("%R/timeline?u=%h&c=%t&nd", zDispUser, zDate);
381
+ @ (user: %z(href("%z",zLink))%h(zDispUser)</a>%s(zTagList?",":"\051")
380382
}else{
381
- @ (user: %h(zUser)%s(zTagList?",":"\051")
383
+ @ (user: %h(zDispUser)%s(zTagList?",":"\051")
382384
}
383385
384386
/* Generate a "detail" link for tags. */
385387
if( (zType[0]=='g' || zType[0]=='w' || zType[0]=='t') && g.perm.Hyperlink ){
386388
@ [%z(href("%R/info/%S",zUuid))details</a>]
387389
--- src/timeline.c
+++ src/timeline.c
@@ -85,10 +85,11 @@
85 ** Generate a hyperlink to a user. This will link to a timeline showing
86 ** events by that user. If the date+time is specified, then the timeline
87 ** is centered on that date+time.
88 */
89 void hyperlink_to_user(const char *zU, const char *zD, const char *zSuf){
 
90 if( zSuf==0 ) zSuf = "";
91 if( g.perm.Hyperlink ){
92 if( zD && zD[0] ){
93 @ %z(href("%R/timeline?c=%T&u=%T",zD,zU))%h(zU)</a>%s(zSuf)
94 }else{
@@ -209,13 +210,13 @@
209
210 zPrevDate[0] = 0;
211 plainText = db_get_int("timeline-plaintext", 0);
212 mxWikiLen = db_get_int("timeline-max-comment", 0);
213 if( db_get_boolean("timeline-block-markup", 0) ){
214 wikiFlags = WIKI_INLINE;
215 }else{
216 wikiFlags = WIKI_INLINE | WIKI_NOBLOCK;
217 }
218 if( tmFlags & TIMELINE_GRAPH ){
219 pGraph = graph_init();
220 /* style is not moved to css, because this is
221 ** a technical div for the timeline graph
@@ -237,10 +238,11 @@
237 const char *zDate = db_column_text(pQuery, 2);
238 const char *zType = db_column_text(pQuery, 7);
239 const char *zUser = db_column_text(pQuery, 4);
240 const char *zTagList = db_column_text(pQuery, 8);
241 int tagid = db_column_int(pQuery, 9);
 
242 const char *zBr = 0; /* Branch */
243 int commentColumn = 3; /* Column containing comment text */
244 int modPending; /* Pending moderation */
245 char zTime[8];
246
@@ -372,15 +374,15 @@
372
373 /* Generate the "user: USERNAME" at the end of the comment, together
374 ** with a hyperlink to another timeline for that user.
375 */
376 if( zTagList && zTagList[0]==0 ) zTagList = 0;
377 if( g.perm.Hyperlink && fossil_strcmp(zUser, zThisUser)!=0 ){
378 char *zLink = mprintf("%R/timeline?u=%h&c=%t&nd", zUser, zDate);
379 @ (user: %z(href("%z",zLink))%h(zUser)</a>%s(zTagList?",":"\051")
380 }else{
381 @ (user: %h(zUser)%s(zTagList?",":"\051")
382 }
383
384 /* Generate a "detail" link for tags. */
385 if( (zType[0]=='g' || zType[0]=='w' || zType[0]=='t') && g.perm.Hyperlink ){
386 @ [%z(href("%R/info/%S",zUuid))details</a>]
387
--- src/timeline.c
+++ src/timeline.c
@@ -85,10 +85,11 @@
85 ** Generate a hyperlink to a user. This will link to a timeline showing
86 ** events by that user. If the date+time is specified, then the timeline
87 ** is centered on that date+time.
88 */
89 void hyperlink_to_user(const char *zU, const char *zD, const char *zSuf){
90 if( zU==0 || zU[0]==0 ) zU = "anonymous";
91 if( zSuf==0 ) zSuf = "";
92 if( g.perm.Hyperlink ){
93 if( zD && zD[0] ){
94 @ %z(href("%R/timeline?c=%T&u=%T",zD,zU))%h(zU)</a>%s(zSuf)
95 }else{
@@ -209,13 +210,13 @@
210
211 zPrevDate[0] = 0;
212 plainText = db_get_int("timeline-plaintext", 0);
213 mxWikiLen = db_get_int("timeline-max-comment", 0);
214 if( db_get_boolean("timeline-block-markup", 0) ){
215 wikiFlags = WIKI_INLINE | WIKI_NOBADLINKS;
216 }else{
217 wikiFlags = WIKI_INLINE | WIKI_NOBLOCK | WIKI_NOBADLINKS;
218 }
219 if( tmFlags & TIMELINE_GRAPH ){
220 pGraph = graph_init();
221 /* style is not moved to css, because this is
222 ** a technical div for the timeline graph
@@ -237,10 +238,11 @@
238 const char *zDate = db_column_text(pQuery, 2);
239 const char *zType = db_column_text(pQuery, 7);
240 const char *zUser = db_column_text(pQuery, 4);
241 const char *zTagList = db_column_text(pQuery, 8);
242 int tagid = db_column_int(pQuery, 9);
243 const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
244 const char *zBr = 0; /* Branch */
245 int commentColumn = 3; /* Column containing comment text */
246 int modPending; /* Pending moderation */
247 char zTime[8];
248
@@ -372,15 +374,15 @@
374
375 /* Generate the "user: USERNAME" at the end of the comment, together
376 ** with a hyperlink to another timeline for that user.
377 */
378 if( zTagList && zTagList[0]==0 ) zTagList = 0;
379 if( g.perm.Hyperlink && fossil_strcmp(zDispUser, zThisUser)!=0 ){
380 char *zLink = mprintf("%R/timeline?u=%h&c=%t&nd", zDispUser, zDate);
381 @ (user: %z(href("%z",zLink))%h(zDispUser)</a>%s(zTagList?",":"\051")
382 }else{
383 @ (user: %h(zDispUser)%s(zTagList?",":"\051")
384 }
385
386 /* Generate a "detail" link for tags. */
387 if( (zType[0]=='g' || zType[0]=='w' || zType[0]=='t') && g.perm.Hyperlink ){
388 @ [%z(href("%R/info/%S",zUuid))details</a>]
389
+13 -7
--- src/tkt.c
+++ src/tkt.c
@@ -430,10 +430,14 @@
430430
int i;
431431
int nJ = 0;
432432
Blob tktchng, cksum;
433433
434434
login_verify_csrf_secret();
435
+ if( !captcha_is_correct() ){
436
+ @ <p class="generalError">Error: Incorrect security code.</p>
437
+ return TH_OK;
438
+ }
435439
zUuid = (const char *)pUuid;
436440
blob_zero(&tktchng);
437441
zDate = date_in_standard_format("now");
438442
blob_appendf(&tktchng, "D %s\n", zDate);
439443
free(zDate);
@@ -522,26 +526,27 @@
522526
if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1);
523527
ticket_init();
524528
getAllTicketFields();
525529
initializeVariablesFromDb();
526530
initializeVariablesFromCGI();
527
- @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><p>
531
+ form_begin(0, "%R/%s", g.zPath);
528532
login_insert_csrf_secret();
529533
if( P("date_override") && g.perm.Setup ){
530534
@ <input type="hidden" name="date_override" value="%h(P("date_override"))">
531535
}
532536
@ </p>
533537
zScript = ticket_newpage_code();
534
- Th_Store("login", g.zLogin);
538
+ Th_Store("login", g.zLogin ? g.zLogin : "nobody");
535539
Th_Store("date", db_text(0, "SELECT datetime('now')"));
536540
Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd,
537541
(void*)&zNewUuid, 0);
538542
if( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br />\n", -1);
539543
if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){
540544
cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zNewUuid));
541545
return;
542546
}
547
+ captcha_generate();
543548
@ </form>
544549
if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);
545550
style_footer();
546551
}
547552
@@ -591,24 +596,25 @@
591596
if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
592597
ticket_init();
593598
getAllTicketFields();
594599
initializeVariablesFromCGI();
595600
initializeVariablesFromDb();
596
- @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><p>
601
+ form_begin(0, "%R/%s", g.zPath);
597602
@ <input type="hidden" name="name" value="%s(zName)" />
598603
login_insert_csrf_secret();
599604
@ </p>
600605
zScript = ticket_editpage_code();
601
- Th_Store("login", g.zLogin);
606
+ Th_Store("login", g.zLogin ? g.zLogin : "nobody");
602607
Th_Store("date", db_text(0, "SELECT datetime('now')"));
603608
Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0);
604609
Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0);
605610
if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br />\n", -1);
606611
if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){
607612
cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zName));
608613
return;
609614
}
615
+ captcha_generate();
610616
@ </form>
611617
if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
612618
style_footer();
613619
}
614620
@@ -829,15 +835,15 @@
829835
const char *z;
830836
z = pTkt->aField[i].zName;
831837
blob_set(&val, pTkt->aField[i].zValue);
832838
if( z[0]=='+' ){
833839
@ <li>Appended to %h(&z[1]):<blockquote>
834
- wiki_convert(&val, 0, 0);
840
+ wiki_convert(&val, 0, WIKI_NOBADLINKS);
835841
@ </blockquote></li>
836842
}else if( blob_size(&val)<=50 && contains_newline(&val) ){
837843
@ <li>Change %h(z) to:<blockquote>
838
- wiki_convert(&val, 0, 0);
844
+ wiki_convert(&val, 0, WIKI_NOBADLINKS);
839845
@ </blockquote></li>
840846
}else{
841847
@ <li>Change %h(z) to "%h(blob_str(&val))"</li>
842848
}
843849
blob_reset(&val);
@@ -1006,11 +1012,11 @@
10061012
eCmd = history;
10071013
}else{
10081014
eCmd = set;
10091015
}
10101016
if( g.argc==3 ){
1011
- usage("set TICKETUUID");
1017
+ usage("set|change|history TICKETUUID");
10121018
}
10131019
zTktUuid = db_text(0,
10141020
"SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*'", g.argv[3]
10151021
);
10161022
if( !zTktUuid ){
10171023
--- src/tkt.c
+++ src/tkt.c
@@ -430,10 +430,14 @@
430 int i;
431 int nJ = 0;
432 Blob tktchng, cksum;
433
434 login_verify_csrf_secret();
 
 
 
 
435 zUuid = (const char *)pUuid;
436 blob_zero(&tktchng);
437 zDate = date_in_standard_format("now");
438 blob_appendf(&tktchng, "D %s\n", zDate);
439 free(zDate);
@@ -522,26 +526,27 @@
522 if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1);
523 ticket_init();
524 getAllTicketFields();
525 initializeVariablesFromDb();
526 initializeVariablesFromCGI();
527 @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><p>
528 login_insert_csrf_secret();
529 if( P("date_override") && g.perm.Setup ){
530 @ <input type="hidden" name="date_override" value="%h(P("date_override"))">
531 }
532 @ </p>
533 zScript = ticket_newpage_code();
534 Th_Store("login", g.zLogin);
535 Th_Store("date", db_text(0, "SELECT datetime('now')"));
536 Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd,
537 (void*)&zNewUuid, 0);
538 if( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br />\n", -1);
539 if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){
540 cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zNewUuid));
541 return;
542 }
 
543 @ </form>
544 if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);
545 style_footer();
546 }
547
@@ -591,24 +596,25 @@
591 if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
592 ticket_init();
593 getAllTicketFields();
594 initializeVariablesFromCGI();
595 initializeVariablesFromDb();
596 @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><p>
597 @ <input type="hidden" name="name" value="%s(zName)" />
598 login_insert_csrf_secret();
599 @ </p>
600 zScript = ticket_editpage_code();
601 Th_Store("login", g.zLogin);
602 Th_Store("date", db_text(0, "SELECT datetime('now')"));
603 Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0);
604 Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0);
605 if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br />\n", -1);
606 if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){
607 cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zName));
608 return;
609 }
 
610 @ </form>
611 if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
612 style_footer();
613 }
614
@@ -829,15 +835,15 @@
829 const char *z;
830 z = pTkt->aField[i].zName;
831 blob_set(&val, pTkt->aField[i].zValue);
832 if( z[0]=='+' ){
833 @ <li>Appended to %h(&z[1]):<blockquote>
834 wiki_convert(&val, 0, 0);
835 @ </blockquote></li>
836 }else if( blob_size(&val)<=50 && contains_newline(&val) ){
837 @ <li>Change %h(z) to:<blockquote>
838 wiki_convert(&val, 0, 0);
839 @ </blockquote></li>
840 }else{
841 @ <li>Change %h(z) to "%h(blob_str(&val))"</li>
842 }
843 blob_reset(&val);
@@ -1006,11 +1012,11 @@
1006 eCmd = history;
1007 }else{
1008 eCmd = set;
1009 }
1010 if( g.argc==3 ){
1011 usage("set TICKETUUID");
1012 }
1013 zTktUuid = db_text(0,
1014 "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*'", g.argv[3]
1015 );
1016 if( !zTktUuid ){
1017
--- src/tkt.c
+++ src/tkt.c
@@ -430,10 +430,14 @@
430 int i;
431 int nJ = 0;
432 Blob tktchng, cksum;
433
434 login_verify_csrf_secret();
435 if( !captcha_is_correct() ){
436 @ <p class="generalError">Error: Incorrect security code.</p>
437 return TH_OK;
438 }
439 zUuid = (const char *)pUuid;
440 blob_zero(&tktchng);
441 zDate = date_in_standard_format("now");
442 blob_appendf(&tktchng, "D %s\n", zDate);
443 free(zDate);
@@ -522,26 +526,27 @@
526 if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1);
527 ticket_init();
528 getAllTicketFields();
529 initializeVariablesFromDb();
530 initializeVariablesFromCGI();
531 form_begin(0, "%R/%s", g.zPath);
532 login_insert_csrf_secret();
533 if( P("date_override") && g.perm.Setup ){
534 @ <input type="hidden" name="date_override" value="%h(P("date_override"))">
535 }
536 @ </p>
537 zScript = ticket_newpage_code();
538 Th_Store("login", g.zLogin ? g.zLogin : "nobody");
539 Th_Store("date", db_text(0, "SELECT datetime('now')"));
540 Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd,
541 (void*)&zNewUuid, 0);
542 if( g.thTrace ) Th_Trace("BEGIN_TKTNEW_SCRIPT<br />\n", -1);
543 if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){
544 cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zNewUuid));
545 return;
546 }
547 captcha_generate();
548 @ </form>
549 if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);
550 style_footer();
551 }
552
@@ -591,24 +596,25 @@
596 if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
597 ticket_init();
598 getAllTicketFields();
599 initializeVariablesFromCGI();
600 initializeVariablesFromDb();
601 form_begin(0, "%R/%s", g.zPath);
602 @ <input type="hidden" name="name" value="%s(zName)" />
603 login_insert_csrf_secret();
604 @ </p>
605 zScript = ticket_editpage_code();
606 Th_Store("login", g.zLogin ? g.zLogin : "nobody");
607 Th_Store("date", db_text(0, "SELECT datetime('now')"));
608 Th_CreateCommand(g.interp, "append_field", appendRemarkCmd, 0, 0);
609 Th_CreateCommand(g.interp, "submit_ticket", submitTicketCmd, (void*)&zName,0);
610 if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT_SCRIPT<br />\n", -1);
611 if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){
612 cgi_redirect(mprintf("%s/tktview/%s", g.zTop, zName));
613 return;
614 }
615 captcha_generate();
616 @ </form>
617 if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
618 style_footer();
619 }
620
@@ -829,15 +835,15 @@
835 const char *z;
836 z = pTkt->aField[i].zName;
837 blob_set(&val, pTkt->aField[i].zValue);
838 if( z[0]=='+' ){
839 @ <li>Appended to %h(&z[1]):<blockquote>
840 wiki_convert(&val, 0, WIKI_NOBADLINKS);
841 @ </blockquote></li>
842 }else if( blob_size(&val)<=50 && contains_newline(&val) ){
843 @ <li>Change %h(z) to:<blockquote>
844 wiki_convert(&val, 0, WIKI_NOBADLINKS);
845 @ </blockquote></li>
846 }else{
847 @ <li>Change %h(z) to "%h(blob_str(&val))"</li>
848 }
849 blob_reset(&val);
@@ -1006,11 +1012,11 @@
1012 eCmd = history;
1013 }else{
1014 eCmd = set;
1015 }
1016 if( g.argc==3 ){
1017 usage("set|change|history TICKETUUID");
1018 }
1019 zTktUuid = db_text(0,
1020 "SELECT tkt_uuid FROM ticket WHERE tkt_uuid GLOB '%s*'", g.argv[3]
1021 );
1022 if( !zTktUuid ){
1023
+3 -1
--- src/update.c
+++ src/update.c
@@ -123,11 +123,13 @@
123123
db_must_be_within_tree();
124124
vid = db_lget_int("checkout", 0);
125125
if( vid==0 ){
126126
fossil_fatal("cannot find current version");
127127
}
128
- if( !nochangeFlag && !internalUpdate ) autosync(AUTOSYNC_PULL);
128
+ if( !nochangeFlag && !internalUpdate ){
129
+ autosync(SYNC_PULL + SYNC_VERBOSE*verboseFlag);
130
+ }
129131
130132
/* Create any empty directories now, as well as after the update,
131133
** so changes in settings are reflected now */
132134
if( !nochangeFlag ) ensure_empty_dirs_created();
133135
134136
--- src/update.c
+++ src/update.c
@@ -123,11 +123,13 @@
123 db_must_be_within_tree();
124 vid = db_lget_int("checkout", 0);
125 if( vid==0 ){
126 fossil_fatal("cannot find current version");
127 }
128 if( !nochangeFlag && !internalUpdate ) autosync(AUTOSYNC_PULL);
 
 
129
130 /* Create any empty directories now, as well as after the update,
131 ** so changes in settings are reflected now */
132 if( !nochangeFlag ) ensure_empty_dirs_created();
133
134
--- src/update.c
+++ src/update.c
@@ -123,11 +123,13 @@
123 db_must_be_within_tree();
124 vid = db_lget_int("checkout", 0);
125 if( vid==0 ){
126 fossil_fatal("cannot find current version");
127 }
128 if( !nochangeFlag && !internalUpdate ){
129 autosync(SYNC_PULL + SYNC_VERBOSE*verboseFlag);
130 }
131
132 /* Create any empty directories now, as well as after the update,
133 ** so changes in settings are reflected now */
134 if( !nochangeFlag ) ensure_empty_dirs_created();
135
136
+37 -11
--- src/wiki.c
+++ src/wiki.c
@@ -127,10 +127,11 @@
127127
*/
128128
void wiki_page(void){
129129
char *zTag;
130130
int rid = 0;
131131
int isSandbox;
132
+ char *zUuid;
132133
Blob wiki;
133134
Manifest *pWiki = 0;
134135
const char *zPageName;
135136
char *zBody = mprintf("%s","<i>Empty Page</i>");
136137
@@ -159,12 +160,13 @@
159160
@ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a>
160161
@ available on this server.</li>
161162
if( g.perm.ModWiki ){
162163
@ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li>
163164
}
164
- @ <li> <form method="get" action="%s(g.zTop)/wfind"><div>
165
- @ Search wiki titles: <input type="text" name="title"/>
165
+ @ <li>
166
+ form_begin(0, "%R/wfind");
167
+ @ <div>Search wiki titles: <input type="text" name="title"/>
166168
@ &nbsp; <input type="submit" /></div></form>
167169
@ </li>
168170
@ </ul>
169171
style_footer();
170172
return;
@@ -171,10 +173,11 @@
171173
}
172174
if( check_name(zPageName) ) return;
173175
isSandbox = is_sandbox(zPageName);
174176
if( isSandbox ){
175177
zBody = db_get("sandbox",zBody);
178
+ rid = 0;
176179
}else{
177180
zTag = mprintf("wiki-%s", zPageName);
178181
rid = db_int(0,
179182
"SELECT rid FROM tagxref"
180183
" WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
@@ -185,10 +188,17 @@
185188
if( pWiki ){
186189
zBody = pWiki->zWiki;
187190
}
188191
}
189192
if( !g.isHome ){
193
+ if( rid ){
194
+ style_submenu_element("Diff", "Last change",
195
+ "%R/wdiff?name=%T&a=%d", zPageName, rid);
196
+ zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
197
+ style_submenu_element("Details", "Details",
198
+ "%R/info/%S", zUuid);
199
+ }
190200
if( (rid && g.perm.WrWiki) || (!rid && g.perm.NewWiki) ){
191201
if( db_get_boolean("wysiwyg-wiki", 0) ){
192202
style_submenu_element("Edit", "Edit Wiki Page",
193203
"%s/wikiedit?name=%T&wysiwyg=1",
194204
g.zTop, zPageName);
@@ -253,10 +263,11 @@
253263
const char *zPageName;
254264
int n;
255265
const char *z;
256266
char *zBody = (char*)P("w");
257267
int isWysiwyg = P("wysiwyg")!=0;
268
+ int goodCaptcha = 1;
258269
259270
if( P("edit-wysiwyg")!=0 ){ isWysiwyg = 1; zBody = 0; }
260271
if( P("edit-markup")!=0 ){ isWysiwyg = 0; zBody = 0; }
261272
if( zBody ){
262273
if( isWysiwyg ){
@@ -294,11 +305,13 @@
294305
}
295306
if( zBody==0 && (pWiki = manifest_get(rid, CFTYPE_WIKI))!=0 ){
296307
zBody = pWiki->zWiki;
297308
}
298309
}
299
- if( P("submit")!=0 && zBody!=0 ){
310
+ if( P("submit")!=0 && zBody!=0
311
+ && (goodCaptcha = captcha_is_correct())
312
+ ){
300313
char *zDate;
301314
Blob cksum;
302315
blob_zero(&wiki);
303316
db_begin_transaction();
304317
if( isSandbox ){
@@ -333,10 +346,13 @@
333346
if( zBody==0 ){
334347
zBody = mprintf("<i>Empty Page</i>");
335348
}
336349
style_set_current_page("%s?name=%T", g.zPath, zPageName);
337350
style_header("Edit: %s", zPageName);
351
+ if( !goodCaptcha ){
352
+ @ <p class="generalError">Error: Incorrect security code.</p>
353
+ }
338354
blob_zero(&wiki);
339355
blob_append(&wiki, zBody, -1);
340356
if( P("preview")!=0 ){
341357
@ Preview:<hr />
342358
wiki_convert(&wiki, 0, 0);
@@ -348,11 +364,12 @@
348364
}
349365
if( n<20 ) n = 20;
350366
if( n>30 ) n = 30;
351367
if( !isWysiwyg ){
352368
/* Traditional markup-only editing */
353
- @ <form method="post" action="%s(g.zTop)/wikiedit"><div>
369
+ form_begin(0, "%R/wikiedit");
370
+ @ <div>
354371
@ <textarea name="w" class="wikiedit" cols="80"
355372
@ rows="%d(n)" wrap="virtual">%h(zBody)</textarea>
356373
@ <br />
357374
if( db_get_boolean("wysiwyg-wiki", 0) ){
358375
@ <input type="submit" name="edit-wysiwyg" value="Wysiwyg Editor"
@@ -360,12 +377,12 @@
360377
}
361378
@ <input type="submit" name="preview" value="Preview Your Changes" />
362379
}else{
363380
/* Wysiwyg editing */
364381
Blob html, temp;
365
- @ <form method="post" action="%s(g.zTop)/wikiedit"
366
- @ onsubmit="wysiwygSubmit()"><div>
382
+ form_begin("onsubmit='wysiwygSubmit()'", "%R/wikiedit");
383
+ @ <div>
367384
@ <input type="hidden" name="wysiwyg" value="1" />
368385
blob_zero(&temp);
369386
wiki_convert(&wiki, &temp, 0);
370387
blob_zero(&html);
371388
htmlTidy(blob_str(&temp), &html);
@@ -374,16 +391,18 @@
374391
blob_reset(&html);
375392
@ <br />
376393
@ <input type="submit" name="edit-markup" value="Markup Editor"
377394
@ onclick='return confirm("Switching to markup-mode\nwill erase your WYSIWYG\nedits. Continue?")' />
378395
}
379
- @ <input type="submit" name="submit" value="Apply These Changes" />
380396
login_insert_csrf_secret();
397
+ @ <input type="submit" name="submit" value="Apply These Changes" />
381398
@ <input type="hidden" name="name" value="%h(zPageName)" />
382399
@ <input type="submit" name="cancel" value="Cancel"
383400
@ onclick='confirm("Abandon your changes?")' />
384
- @ </div></form>
401
+ @ </div>
402
+ captcha_generate();
403
+ @ </form>
385404
manifest_destroy(pWiki);
386405
blob_reset(&wiki);
387406
style_footer();
388407
}
389408
@@ -410,11 +429,11 @@
410429
}
411430
}
412431
style_header("Create A New Wiki Page");
413432
@ <p>Rules for wiki page names:</p>
414433
well_formed_wiki_name_rules();
415
- @ <form method="post" action="%s(g.zTop)/wikinew">
434
+ form_begin(0, "%R/wikinew");
416435
@ <p>Name of new wiki page:
417436
@ <input style="width: 35;" type="text" name="name" value="%h(zName)" />
418437
@ <input type="submit" value="Create" />
419438
@ </p></form>
420439
if( zName[0] ){
@@ -455,10 +474,11 @@
455474
char *zTag;
456475
int rid = 0;
457476
int isSandbox;
458477
const char *zPageName;
459478
const char *zUser;
479
+ int goodCaptcha = 1;
460480
461481
login_check_credentials();
462482
zPageName = PD("name","");
463483
if( check_name(zPageName) ) return;
464484
isSandbox = is_sandbox(zPageName);
@@ -477,11 +497,13 @@
477497
}
478498
if( !g.perm.ApndWiki ){
479499
login_needed();
480500
return;
481501
}
482
- if( P("submit")!=0 && P("r")!=0 && P("u")!=0 ){
502
+ if( P("submit")!=0 && P("r")!=0 && P("u")!=0
503
+ && (goodCaptcha = captcha_is_correct())
504
+ ){
483505
char *zDate;
484506
Blob cksum;
485507
Blob body;
486508
Blob wiki;
487509
Manifest *pWiki = 0;
@@ -525,10 +547,13 @@
525547
cgi_redirectf("wiki?name=%T", zPageName);
526548
return;
527549
}
528550
style_set_current_page("%s?name=%T", g.zPath, zPageName);
529551
style_header("Append Comment To: %s", zPageName);
552
+ if( !goodCaptcha ){
553
+ @ <p class="generalError">Error: Incorrect security code.</p>
554
+ }
530555
if( P("preview")!=0 ){
531556
Blob preview;
532557
blob_zero(&preview);
533558
appendRemark(&preview);
534559
@ Preview:<hr>
@@ -535,11 +560,11 @@
535560
wiki_convert(&preview, 0, 0);
536561
@ <hr>
537562
blob_reset(&preview);
538563
}
539564
zUser = PD("u", g.zLogin);
540
- @ <form method="post" action="%s(g.zTop)/wikiappend">
565
+ form_begin(0, "%R/wikiappend");
541566
login_insert_csrf_secret();
542567
@ <input type="hidden" name="name" value="%h(zPageName)" />
543568
@ Your Name:
544569
@ <input type="text" name="u" size="20" value="%h(zUser)" /><br />
545570
@ Comment to append:<br />
@@ -547,10 +572,11 @@
547572
@ rows="10" wrap="virtual">%h(PD("r",""))</textarea>
548573
@ <br />
549574
@ <input type="submit" name="preview" value="Preview Your Comment" />
550575
@ <input type="submit" name="submit" value="Append Your Changes" />
551576
@ <input type="submit" name="cancel" value="Cancel" />
577
+ captcha_generate();
552578
@ </form>
553579
style_footer();
554580
}
555581
556582
/*
557583
--- src/wiki.c
+++ src/wiki.c
@@ -127,10 +127,11 @@
127 */
128 void wiki_page(void){
129 char *zTag;
130 int rid = 0;
131 int isSandbox;
 
132 Blob wiki;
133 Manifest *pWiki = 0;
134 const char *zPageName;
135 char *zBody = mprintf("%s","<i>Empty Page</i>");
136
@@ -159,12 +160,13 @@
159 @ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a>
160 @ available on this server.</li>
161 if( g.perm.ModWiki ){
162 @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li>
163 }
164 @ <li> <form method="get" action="%s(g.zTop)/wfind"><div>
165 @ Search wiki titles: <input type="text" name="title"/>
 
166 @ &nbsp; <input type="submit" /></div></form>
167 @ </li>
168 @ </ul>
169 style_footer();
170 return;
@@ -171,10 +173,11 @@
171 }
172 if( check_name(zPageName) ) return;
173 isSandbox = is_sandbox(zPageName);
174 if( isSandbox ){
175 zBody = db_get("sandbox",zBody);
 
176 }else{
177 zTag = mprintf("wiki-%s", zPageName);
178 rid = db_int(0,
179 "SELECT rid FROM tagxref"
180 " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
@@ -185,10 +188,17 @@
185 if( pWiki ){
186 zBody = pWiki->zWiki;
187 }
188 }
189 if( !g.isHome ){
 
 
 
 
 
 
 
190 if( (rid && g.perm.WrWiki) || (!rid && g.perm.NewWiki) ){
191 if( db_get_boolean("wysiwyg-wiki", 0) ){
192 style_submenu_element("Edit", "Edit Wiki Page",
193 "%s/wikiedit?name=%T&wysiwyg=1",
194 g.zTop, zPageName);
@@ -253,10 +263,11 @@
253 const char *zPageName;
254 int n;
255 const char *z;
256 char *zBody = (char*)P("w");
257 int isWysiwyg = P("wysiwyg")!=0;
 
258
259 if( P("edit-wysiwyg")!=0 ){ isWysiwyg = 1; zBody = 0; }
260 if( P("edit-markup")!=0 ){ isWysiwyg = 0; zBody = 0; }
261 if( zBody ){
262 if( isWysiwyg ){
@@ -294,11 +305,13 @@
294 }
295 if( zBody==0 && (pWiki = manifest_get(rid, CFTYPE_WIKI))!=0 ){
296 zBody = pWiki->zWiki;
297 }
298 }
299 if( P("submit")!=0 && zBody!=0 ){
 
 
300 char *zDate;
301 Blob cksum;
302 blob_zero(&wiki);
303 db_begin_transaction();
304 if( isSandbox ){
@@ -333,10 +346,13 @@
333 if( zBody==0 ){
334 zBody = mprintf("<i>Empty Page</i>");
335 }
336 style_set_current_page("%s?name=%T", g.zPath, zPageName);
337 style_header("Edit: %s", zPageName);
 
 
 
338 blob_zero(&wiki);
339 blob_append(&wiki, zBody, -1);
340 if( P("preview")!=0 ){
341 @ Preview:<hr />
342 wiki_convert(&wiki, 0, 0);
@@ -348,11 +364,12 @@
348 }
349 if( n<20 ) n = 20;
350 if( n>30 ) n = 30;
351 if( !isWysiwyg ){
352 /* Traditional markup-only editing */
353 @ <form method="post" action="%s(g.zTop)/wikiedit"><div>
 
354 @ <textarea name="w" class="wikiedit" cols="80"
355 @ rows="%d(n)" wrap="virtual">%h(zBody)</textarea>
356 @ <br />
357 if( db_get_boolean("wysiwyg-wiki", 0) ){
358 @ <input type="submit" name="edit-wysiwyg" value="Wysiwyg Editor"
@@ -360,12 +377,12 @@
360 }
361 @ <input type="submit" name="preview" value="Preview Your Changes" />
362 }else{
363 /* Wysiwyg editing */
364 Blob html, temp;
365 @ <form method="post" action="%s(g.zTop)/wikiedit"
366 @ onsubmit="wysiwygSubmit()"><div>
367 @ <input type="hidden" name="wysiwyg" value="1" />
368 blob_zero(&temp);
369 wiki_convert(&wiki, &temp, 0);
370 blob_zero(&html);
371 htmlTidy(blob_str(&temp), &html);
@@ -374,16 +391,18 @@
374 blob_reset(&html);
375 @ <br />
376 @ <input type="submit" name="edit-markup" value="Markup Editor"
377 @ onclick='return confirm("Switching to markup-mode\nwill erase your WYSIWYG\nedits. Continue?")' />
378 }
379 @ <input type="submit" name="submit" value="Apply These Changes" />
380 login_insert_csrf_secret();
 
381 @ <input type="hidden" name="name" value="%h(zPageName)" />
382 @ <input type="submit" name="cancel" value="Cancel"
383 @ onclick='confirm("Abandon your changes?")' />
384 @ </div></form>
 
 
385 manifest_destroy(pWiki);
386 blob_reset(&wiki);
387 style_footer();
388 }
389
@@ -410,11 +429,11 @@
410 }
411 }
412 style_header("Create A New Wiki Page");
413 @ <p>Rules for wiki page names:</p>
414 well_formed_wiki_name_rules();
415 @ <form method="post" action="%s(g.zTop)/wikinew">
416 @ <p>Name of new wiki page:
417 @ <input style="width: 35;" type="text" name="name" value="%h(zName)" />
418 @ <input type="submit" value="Create" />
419 @ </p></form>
420 if( zName[0] ){
@@ -455,10 +474,11 @@
455 char *zTag;
456 int rid = 0;
457 int isSandbox;
458 const char *zPageName;
459 const char *zUser;
 
460
461 login_check_credentials();
462 zPageName = PD("name","");
463 if( check_name(zPageName) ) return;
464 isSandbox = is_sandbox(zPageName);
@@ -477,11 +497,13 @@
477 }
478 if( !g.perm.ApndWiki ){
479 login_needed();
480 return;
481 }
482 if( P("submit")!=0 && P("r")!=0 && P("u")!=0 ){
 
 
483 char *zDate;
484 Blob cksum;
485 Blob body;
486 Blob wiki;
487 Manifest *pWiki = 0;
@@ -525,10 +547,13 @@
525 cgi_redirectf("wiki?name=%T", zPageName);
526 return;
527 }
528 style_set_current_page("%s?name=%T", g.zPath, zPageName);
529 style_header("Append Comment To: %s", zPageName);
 
 
 
530 if( P("preview")!=0 ){
531 Blob preview;
532 blob_zero(&preview);
533 appendRemark(&preview);
534 @ Preview:<hr>
@@ -535,11 +560,11 @@
535 wiki_convert(&preview, 0, 0);
536 @ <hr>
537 blob_reset(&preview);
538 }
539 zUser = PD("u", g.zLogin);
540 @ <form method="post" action="%s(g.zTop)/wikiappend">
541 login_insert_csrf_secret();
542 @ <input type="hidden" name="name" value="%h(zPageName)" />
543 @ Your Name:
544 @ <input type="text" name="u" size="20" value="%h(zUser)" /><br />
545 @ Comment to append:<br />
@@ -547,10 +572,11 @@
547 @ rows="10" wrap="virtual">%h(PD("r",""))</textarea>
548 @ <br />
549 @ <input type="submit" name="preview" value="Preview Your Comment" />
550 @ <input type="submit" name="submit" value="Append Your Changes" />
551 @ <input type="submit" name="cancel" value="Cancel" />
 
552 @ </form>
553 style_footer();
554 }
555
556 /*
557
--- src/wiki.c
+++ src/wiki.c
@@ -127,10 +127,11 @@
127 */
128 void wiki_page(void){
129 char *zTag;
130 int rid = 0;
131 int isSandbox;
132 char *zUuid;
133 Blob wiki;
134 Manifest *pWiki = 0;
135 const char *zPageName;
136 char *zBody = mprintf("%s","<i>Empty Page</i>");
137
@@ -159,12 +160,13 @@
160 @ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a>
161 @ available on this server.</li>
162 if( g.perm.ModWiki ){
163 @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li>
164 }
165 @ <li>
166 form_begin(0, "%R/wfind");
167 @ <div>Search wiki titles: <input type="text" name="title"/>
168 @ &nbsp; <input type="submit" /></div></form>
169 @ </li>
170 @ </ul>
171 style_footer();
172 return;
@@ -171,10 +173,11 @@
173 }
174 if( check_name(zPageName) ) return;
175 isSandbox = is_sandbox(zPageName);
176 if( isSandbox ){
177 zBody = db_get("sandbox",zBody);
178 rid = 0;
179 }else{
180 zTag = mprintf("wiki-%s", zPageName);
181 rid = db_int(0,
182 "SELECT rid FROM tagxref"
183 " WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
@@ -185,10 +188,17 @@
188 if( pWiki ){
189 zBody = pWiki->zWiki;
190 }
191 }
192 if( !g.isHome ){
193 if( rid ){
194 style_submenu_element("Diff", "Last change",
195 "%R/wdiff?name=%T&a=%d", zPageName, rid);
196 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
197 style_submenu_element("Details", "Details",
198 "%R/info/%S", zUuid);
199 }
200 if( (rid && g.perm.WrWiki) || (!rid && g.perm.NewWiki) ){
201 if( db_get_boolean("wysiwyg-wiki", 0) ){
202 style_submenu_element("Edit", "Edit Wiki Page",
203 "%s/wikiedit?name=%T&wysiwyg=1",
204 g.zTop, zPageName);
@@ -253,10 +263,11 @@
263 const char *zPageName;
264 int n;
265 const char *z;
266 char *zBody = (char*)P("w");
267 int isWysiwyg = P("wysiwyg")!=0;
268 int goodCaptcha = 1;
269
270 if( P("edit-wysiwyg")!=0 ){ isWysiwyg = 1; zBody = 0; }
271 if( P("edit-markup")!=0 ){ isWysiwyg = 0; zBody = 0; }
272 if( zBody ){
273 if( isWysiwyg ){
@@ -294,11 +305,13 @@
305 }
306 if( zBody==0 && (pWiki = manifest_get(rid, CFTYPE_WIKI))!=0 ){
307 zBody = pWiki->zWiki;
308 }
309 }
310 if( P("submit")!=0 && zBody!=0
311 && (goodCaptcha = captcha_is_correct())
312 ){
313 char *zDate;
314 Blob cksum;
315 blob_zero(&wiki);
316 db_begin_transaction();
317 if( isSandbox ){
@@ -333,10 +346,13 @@
346 if( zBody==0 ){
347 zBody = mprintf("<i>Empty Page</i>");
348 }
349 style_set_current_page("%s?name=%T", g.zPath, zPageName);
350 style_header("Edit: %s", zPageName);
351 if( !goodCaptcha ){
352 @ <p class="generalError">Error: Incorrect security code.</p>
353 }
354 blob_zero(&wiki);
355 blob_append(&wiki, zBody, -1);
356 if( P("preview")!=0 ){
357 @ Preview:<hr />
358 wiki_convert(&wiki, 0, 0);
@@ -348,11 +364,12 @@
364 }
365 if( n<20 ) n = 20;
366 if( n>30 ) n = 30;
367 if( !isWysiwyg ){
368 /* Traditional markup-only editing */
369 form_begin(0, "%R/wikiedit");
370 @ <div>
371 @ <textarea name="w" class="wikiedit" cols="80"
372 @ rows="%d(n)" wrap="virtual">%h(zBody)</textarea>
373 @ <br />
374 if( db_get_boolean("wysiwyg-wiki", 0) ){
375 @ <input type="submit" name="edit-wysiwyg" value="Wysiwyg Editor"
@@ -360,12 +377,12 @@
377 }
378 @ <input type="submit" name="preview" value="Preview Your Changes" />
379 }else{
380 /* Wysiwyg editing */
381 Blob html, temp;
382 form_begin("onsubmit='wysiwygSubmit()'", "%R/wikiedit");
383 @ <div>
384 @ <input type="hidden" name="wysiwyg" value="1" />
385 blob_zero(&temp);
386 wiki_convert(&wiki, &temp, 0);
387 blob_zero(&html);
388 htmlTidy(blob_str(&temp), &html);
@@ -374,16 +391,18 @@
391 blob_reset(&html);
392 @ <br />
393 @ <input type="submit" name="edit-markup" value="Markup Editor"
394 @ onclick='return confirm("Switching to markup-mode\nwill erase your WYSIWYG\nedits. Continue?")' />
395 }
 
396 login_insert_csrf_secret();
397 @ <input type="submit" name="submit" value="Apply These Changes" />
398 @ <input type="hidden" name="name" value="%h(zPageName)" />
399 @ <input type="submit" name="cancel" value="Cancel"
400 @ onclick='confirm("Abandon your changes?")' />
401 @ </div>
402 captcha_generate();
403 @ </form>
404 manifest_destroy(pWiki);
405 blob_reset(&wiki);
406 style_footer();
407 }
408
@@ -410,11 +429,11 @@
429 }
430 }
431 style_header("Create A New Wiki Page");
432 @ <p>Rules for wiki page names:</p>
433 well_formed_wiki_name_rules();
434 form_begin(0, "%R/wikinew");
435 @ <p>Name of new wiki page:
436 @ <input style="width: 35;" type="text" name="name" value="%h(zName)" />
437 @ <input type="submit" value="Create" />
438 @ </p></form>
439 if( zName[0] ){
@@ -455,10 +474,11 @@
474 char *zTag;
475 int rid = 0;
476 int isSandbox;
477 const char *zPageName;
478 const char *zUser;
479 int goodCaptcha = 1;
480
481 login_check_credentials();
482 zPageName = PD("name","");
483 if( check_name(zPageName) ) return;
484 isSandbox = is_sandbox(zPageName);
@@ -477,11 +497,13 @@
497 }
498 if( !g.perm.ApndWiki ){
499 login_needed();
500 return;
501 }
502 if( P("submit")!=0 && P("r")!=0 && P("u")!=0
503 && (goodCaptcha = captcha_is_correct())
504 ){
505 char *zDate;
506 Blob cksum;
507 Blob body;
508 Blob wiki;
509 Manifest *pWiki = 0;
@@ -525,10 +547,13 @@
547 cgi_redirectf("wiki?name=%T", zPageName);
548 return;
549 }
550 style_set_current_page("%s?name=%T", g.zPath, zPageName);
551 style_header("Append Comment To: %s", zPageName);
552 if( !goodCaptcha ){
553 @ <p class="generalError">Error: Incorrect security code.</p>
554 }
555 if( P("preview")!=0 ){
556 Blob preview;
557 blob_zero(&preview);
558 appendRemark(&preview);
559 @ Preview:<hr>
@@ -535,11 +560,11 @@
560 wiki_convert(&preview, 0, 0);
561 @ <hr>
562 blob_reset(&preview);
563 }
564 zUser = PD("u", g.zLogin);
565 form_begin(0, "%R/wikiappend");
566 login_insert_csrf_secret();
567 @ <input type="hidden" name="name" value="%h(zPageName)" />
568 @ Your Name:
569 @ <input type="text" name="u" size="20" value="%h(zUser)" /><br />
570 @ Comment to append:<br />
@@ -547,10 +572,11 @@
572 @ rows="10" wrap="virtual">%h(PD("r",""))</textarea>
573 @ <br />
574 @ <input type="submit" name="preview" value="Preview Your Comment" />
575 @ <input type="submit" name="submit" value="Append Your Changes" />
576 @ <input type="submit" name="cancel" value="Cancel" />
577 captcha_generate();
578 @ </form>
579 style_footer();
580 }
581
582 /*
583
+88 -56
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -23,17 +23,16 @@
2323
2424
#if INTERFACE
2525
/*
2626
** Allowed wiki transformation operations
2727
*/
28
-#define WIKI_NOFOLLOW 0x001
29
-#define WIKI_HTML 0x002
30
-#define WIKI_INLINE 0x004 /* Do not surround with <p>..</p> */
31
-#define WIKI_NOBLOCK 0x008 /* No block markup of any kind */
32
-#define WIKI_BUTTONS 0x010 /* Allow sub-menu buttons */
33
-#define WIKI_NOBADLINKS 0x020 /* Ignore broken hyperlinks */
34
-#define WIKI_DECORATEONLY 0x040 /* No markup. Only decorate links */
28
+#define WIKI_HTMLONLY 0x001 /* HTML markup only. No wiki */
29
+#define WIKI_INLINE 0x002 /* Do not surround with <p>..</p> */
30
+#define WIKI_NOBLOCK 0x004 /* No block markup of any kind */
31
+#define WIKI_BUTTONS 0x008 /* Allow sub-menu buttons */
32
+#define WIKI_NOBADLINKS 0x010 /* Ignore broken hyperlinks */
33
+#define WIKI_LINKSONLY 0x020 /* No markup. Only decorate links */
3534
#endif
3635
3736
3837
/*
3938
** These are the only markup attributes allowed.
@@ -386,14 +385,14 @@
386385
** State flags. Save the lower 16 bits for the WIKI_* flags.
387386
*/
388387
#define AT_NEWLINE 0x0010000 /* At start of a line */
389388
#define AT_PARAGRAPH 0x0020000 /* At start of a paragraph */
390389
#define ALLOW_WIKI 0x0040000 /* Allow wiki markup */
391
-#define FONT_MARKUP_ONLY 0x0080000 /* Only allow MUTYPE_FONT markup */
392
-#define INLINE_MARKUP_ONLY 0x0100000 /* Allow only "inline" markup */
393
-#define IN_LIST 0x0200000 /* Within wiki <ul> or <ol> */
394
-#define WIKI_USE_HTML 0x0400000 /* wiki-use-html option = on */
390
+#define ALLOW_LINKS 0x0080000 /* Allow [...] hyperlinks */
391
+#define FONT_MARKUP_ONLY 0x0100000 /* Only allow MUTYPE_FONT markup */
392
+#define INLINE_MARKUP_ONLY 0x0200000 /* Allow only "inline" markup */
393
+#define IN_LIST 0x0400000 /* Within wiki <ul> or <ol> */
395394
396395
/*
397396
** Current state of the rendering engine
398397
*/
399398
typedef struct Renderer Renderer;
@@ -487,18 +486,27 @@
487486
** <
488487
** &
489488
** \n
490489
** [
491490
**
492
-** The "[" and "\n" are only considered interesting if the "useWiki"
493
-** flag is set.
491
+** The "[" is only considered if flags contain ALLOW_LINKS or ALLOW_WIKI.
492
+** The "\n" is only considered interesting if the flags constains ALLOW_WIKI.
494493
*/
495
-static int textLength(const char *z, int useWiki){
494
+static int textLength(const char *z, int flags){
496495
int n = 0;
497
- int c;
498
- while( (c = z[0])!=0 && c!='<' && c!='&' &&
499
- (useWiki==0 || (c!='[' && c!='\n')) ){
496
+ int c, x1, x2;
497
+
498
+ if( flags & ALLOW_WIKI ){
499
+ x1 = '[';
500
+ x2 = '\n';
501
+ }else if( flags & ALLOW_LINKS ){
502
+ x1 = '[';
503
+ x2 = 0;
504
+ }else{
505
+ x1 = x2 = 0;
506
+ }
507
+ while( (c = z[0])!=0 && c!='<' && c!='&' && c!=x1 && c!=x2 ){
500508
n++;
501509
z++;
502510
}
503511
return n;
504512
}
@@ -671,13 +679,16 @@
671679
}
672680
if( z[0]=='[' && (n = linkLength(z))>0 ){
673681
*pTokenType = TOKEN_LINK;
674682
return n;
675683
}
684
+ }else if( (p->state & ALLOW_LINKS)!=0 && z[0]=='[' && (n = linkLength(z))>0 ){
685
+ *pTokenType = TOKEN_LINK;
686
+ return n;
676687
}
677688
*pTokenType = TOKEN_TEXT;
678
- return 1 + textLength(z+1, p->state & ALLOW_WIKI);
689
+ return 1 + textLength(z+1, p->state);
679690
}
680691
681692
/*
682693
** Parse only Wiki links, return everything else as TOKEN_RAW.
683694
**
@@ -985,10 +996,11 @@
985996
/*
986997
** Begin a new paragraph if that something that is needed.
987998
*/
988999
static void startAutoParagraph(Renderer *p){
9891000
if( p->wantAutoParagraph==0 ) return;
1001
+ if( p->state & WIKI_LINKSONLY ) return;
9901002
if( p->wikiList==MARKUP_OL || p->wikiList==MARKUP_UL ) return;
9911003
blob_appendf(p->pOut, "<p>", -1);
9921004
pushStack(p, MARKUP_P);
9931005
p->wantAutoParagraph = 0;
9941006
p->inAutoParagraph = 1;
@@ -1084,18 +1096,20 @@
10841096
/*
10851097
** Return a pointer to the name part of zTarget (skipping the "wiki:" prefix
10861098
** if there is one) if zTarget is a valid wiki page name. Return NULL if
10871099
** zTarget names a page that does not exist.
10881100
*/
1089
-static const char *validWikiPageName(const char *zTarget){
1101
+static const char *validWikiPageName(Renderer *p, const char *zTarget){
10901102
if( strncmp(zTarget, "wiki:", 5)==0
10911103
&& wiki_name_is_wellformed((const unsigned char*)zTarget) ){
10921104
return zTarget+5;
10931105
}
10941106
if( strcmp(zTarget, "Sandbox")==0 ) return zTarget;
10951107
if( wiki_name_is_wellformed((const unsigned char *)zTarget)
1096
- && db_exists("SELECT 1 FROM tag WHERE tagname GLOB 'wiki-%q'", zTarget) ){
1108
+ && ((p->state & WIKI_NOBADLINKS)==0 ||
1109
+ db_exists("SELECT 1 FROM tag WHERE tagname GLOB 'wiki-%q'", zTarget))
1110
+ ){
10971111
return zTarget;
10981112
}
10991113
return 0;
11001114
}
11011115
@@ -1145,19 +1159,14 @@
11451159
|| strncmp(zTarget, "https:", 6)==0
11461160
|| strncmp(zTarget, "ftp:", 4)==0
11471161
|| strncmp(zTarget, "mailto:", 7)==0
11481162
){
11491163
blob_appendf(p->pOut, "<a href=\"%s\">", zTarget);
1150
- /* zTerm = "&#x27FE;</a>"; // doesn't work on windows */
11511164
}else if( zTarget[0]=='/' ){
11521165
blob_appendf(p->pOut, "<a href=\"%s%h\">", g.zTop, zTarget);
11531166
}else if( zTarget[0]=='.' || zTarget[0]=='#' ){
1154
- if( 1 ){
1155
- blob_appendf(p->pOut, "<a href=\"%h\">", zTarget);
1156
- }else{
1157
- zTerm = "";
1158
- }
1167
+ blob_appendf(p->pOut, "<a href=\"%h\">", zTarget);
11591168
}else if( is_valid_uuid(zTarget) ){
11601169
int isClosed = 0;
11611170
if( is_ticket(zTarget, &isClosed) ){
11621171
/* Special display processing for tickets. Display the hyperlink
11631172
** as crossed out if the ticket is closed.
@@ -1181,11 +1190,11 @@
11811190
blob_appendf(p->pOut, "[");
11821191
zTerm = "]";
11831192
}
11841193
}
11851194
}else if( !in_this_repo(zTarget) ){
1186
- if( (p->state & (WIKI_DECORATEONLY|WIKI_NOBADLINKS))!=0 ){
1195
+ if( (p->state & (WIKI_LINKSONLY|WIKI_NOBADLINKS))!=0 ){
11871196
zTerm = "";
11881197
}else{
11891198
blob_appendf(p->pOut, "<span class=\"brokenlink\">[", zTarget);
11901199
zTerm = "]</span>";
11911200
}
@@ -1194,16 +1203,16 @@
11941203
zTerm = "]</a>";
11951204
}
11961205
}else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-'
11971206
&& db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){
11981207
blob_appendf(p->pOut, "<a href=\"%R/timeline?c=%T\">", zTarget);
1199
- }else if( (z = validWikiPageName(zTarget))!=0 ){
1208
+ }else if( (z = validWikiPageName(p, zTarget))!=0 ){
12001209
blob_appendf(p->pOut, "<a href=\"%R/wiki?name=%T\">", z);
12011210
}else if( zTarget>=&zOrig[2] && !fossil_isspace(zTarget[-2]) ){
12021211
/* Probably an array subscript in code */
12031212
zTerm = "";
1204
- }else if( (p->state & (WIKI_NOBADLINKS|WIKI_DECORATEONLY))!=0 ){
1213
+ }else if( (p->state & (WIKI_NOBADLINKS|WIKI_LINKSONLY))!=0 ){
12051214
zTerm = "";
12061215
}else{
12071216
blob_appendf(p->pOut, "<span class=\"brokenlink\">[%h]", zTarget);
12081217
zTerm = "</span>";
12091218
}
@@ -1243,19 +1252,20 @@
12431252
static void wiki_render(Renderer *p, char *z){
12441253
int tokenType;
12451254
ParsedMarkup markup;
12461255
int n;
12471256
int inlineOnly = (p->state & INLINE_MARKUP_ONLY)!=0;
1248
- int wikiUseHtml = (p->state & WIKI_USE_HTML)!=0;
1257
+ int wikiHtmlOnly = (p->state & (WIKI_HTMLONLY | WIKI_LINKSONLY))!=0;
1258
+ int linksOnly = (p->state & WIKI_LINKSONLY)!=0;
12491259
char *zOrig = z;
12501260
12511261
/* Make sure the attribute constants and names still align
12521262
** following changes in the attribute list. */
12531263
assert( fossil_strcmp(aAttribute[ATTR_WIDTH].zName, "width")==0 );
12541264
12551265
while( z[0] ){
1256
- if( wikiUseHtml ){
1266
+ if( wikiHtmlOnly ){
12571267
n = nextRawToken(z, p, &tokenType);
12581268
}else{
12591269
n = nextWikiToken(z, p, &tokenType);
12601270
}
12611271
p->state &= ~(AT_NEWLINE|AT_PARAGRAPH);
@@ -1372,33 +1382,37 @@
13721382
startAutoParagraph(p);
13731383
zTarget = &z[1];
13741384
for(i=1; z[i] && z[i]!=']'; i++){
13751385
if( z[i]=='|' && zDisplay==0 ){
13761386
zDisplay = &z[i+1];
1377
- iS1 = i;
1378
- cS1 = z[i];
1379
- z[i] = 0;
1380
- for(j=i-1; j>0 && fossil_isspace(z[j]); j--){ z[j] = 0; }
1387
+ for(j=i; j>0 && fossil_isspace(z[j-1]); j--){}
1388
+ iS1 = j;
1389
+ cS1 = z[j];
1390
+ z[j] = 0;
13811391
}
13821392
}
13831393
z[i] = 0;
13841394
if( zDisplay==0 ){
13851395
zDisplay = zTarget;
13861396
}else{
13871397
while( fossil_isspace(*zDisplay) ) zDisplay++;
13881398
}
13891399
openHyperlink(p, zTarget, zClose, sizeof(zClose), zOrig);
1390
- if( zClose[0] ){
1400
+ if( linksOnly || zClose[0]==0 || p->inVerbatim ){
1401
+ if( cS1 ) z[iS1] = cS1;
1402
+ if( zClose[0]!=']' ){
1403
+ blob_appendf(p->pOut, "[%h]%s", zTarget, zClose);
1404
+ }else{
1405
+ blob_appendf(p->pOut, "%h%s", zTarget, zClose);
1406
+ }
1407
+ }else{
13911408
savedState = p->state;
13921409
p->state &= ~ALLOW_WIKI;
13931410
p->state |= FONT_MARKUP_ONLY;
13941411
wiki_render(p, zDisplay);
13951412
p->state = savedState;
13961413
blob_append(p->pOut, zClose, -1);
1397
- }else{
1398
- if( cS1 ) z[iS1] = cS1;
1399
- blob_appendf(p->pOut, "[%h]", zTarget);
14001414
}
14011415
break;
14021416
}
14031417
case TOKEN_TEXT: {
14041418
int i;
@@ -1406,11 +1420,15 @@
14061420
if( i<n ) startAutoParagraph(p);
14071421
blob_append(p->pOut, z, n);
14081422
break;
14091423
}
14101424
case TOKEN_RAW: {
1411
- blob_append(p->pOut, z, n);
1425
+ if( linksOnly ){
1426
+ htmlize_to_blob(p->pOut, z, n);
1427
+ }else{
1428
+ blob_append(p->pOut, z, n);
1429
+ }
14121430
break;
14131431
}
14141432
case TOKEN_MARKUP: {
14151433
const char *zId;
14161434
int iDiv;
@@ -1501,22 +1519,26 @@
15011519
/* Enter <verbatim> processing. With verbatim enabled, all other
15021520
** markup other than the corresponding end-tag with the same ID is
15031521
** ignored.
15041522
*/
15051523
if( markup.iCode==MARKUP_VERBATIM ){
1506
- int vAttrIdx, vAttrDidAppend=0;
1524
+ int ii, vAttrDidAppend=0;
15071525
p->zVerbatimId = 0;
15081526
p->inVerbatim = 1;
15091527
p->preVerbState = p->state;
15101528
p->state &= ~ALLOW_WIKI;
1511
- for (vAttrIdx = 0; vAttrIdx < markup.nAttr; vAttrIdx++){
1512
- if( markup.aAttr[vAttrIdx].iACode == ATTR_ID ){
1513
- p->zVerbatimId = markup.aAttr[0].zValue;
1514
- }else if( markup.aAttr[vAttrIdx].iACode == ATTR_TYPE ){
1515
- blob_appendf(p->pOut, "<pre name='code' class='%s'>",
1516
- markup.aAttr[vAttrIdx].zValue);
1517
- vAttrDidAppend=1;
1529
+ for(ii=0; ii<markup.nAttr; ii++){
1530
+ if( markup.aAttr[ii].iACode == ATTR_ID ){
1531
+ p->zVerbatimId = markup.aAttr[ii].zValue;
1532
+ }else if( markup.aAttr[ii].iACode == ATTR_TYPE ){
1533
+ if( fossil_stricmp(markup.aAttr[ii].zValue, "allow-links")==0 ){
1534
+ p->state |= ALLOW_LINKS;
1535
+ }else{
1536
+ blob_appendf(p->pOut, "<pre name='code' class='%s'>",
1537
+ markup.aAttr[ii].zValue);
1538
+ vAttrDidAppend=1;
1539
+ }
15181540
}
15191541
}
15201542
if( !vAttrDidAppend ) {
15211543
endAutoParagraph(p);
15221544
blob_append(p->pOut, "<pre class='verbatim'>",-1);
@@ -1592,13 +1614,10 @@
15921614
** reply.
15931615
*/
15941616
void wiki_convert(Blob *pIn, Blob *pOut, int flags){
15951617
Renderer renderer;
15961618
1597
- /* Never show bad hyperlinks */
1598
- flags |= WIKI_NOBADLINKS;
1599
-
16001619
memset(&renderer, 0, sizeof(renderer));
16011620
renderer.renderFlags = flags;
16021621
renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH|flags;
16031622
if( flags & WIKI_NOBLOCK ){
16041623
renderer.state |= INLINE_MARKUP_ONLY;
@@ -1607,24 +1626,26 @@
16071626
renderer.wantAutoParagraph = 0;
16081627
}else{
16091628
renderer.wantAutoParagraph = 1;
16101629
}
16111630
if( wikiUsesHtml() ){
1612
- renderer.state |= WIKI_USE_HTML;
1631
+ renderer.state |= WIKI_HTMLONLY;
16131632
}
16141633
if( pOut ){
16151634
renderer.pOut = pOut;
16161635
}else{
16171636
renderer.pOut = cgi_output_blob();
16181637
}
16191638
16201639
blob_strip_bom(pIn, 0);
1640
+ if( flags & WIKI_LINKSONLY ) blob_append(renderer.pOut, "<pre>", 5);
16211641
wiki_render(&renderer, blob_str(pIn));
16221642
endAutoParagraph(&renderer);
16231643
while( renderer.nStack ){
16241644
popStack(&renderer);
16251645
}
1646
+ if( flags & WIKI_LINKSONLY ) blob_append(renderer.pOut, "</pre>", 5);
16261647
blob_append(renderer.pOut, "\n", 1);
16271648
free(renderer.aStack);
16281649
}
16291650
16301651
/*
@@ -1632,15 +1653,26 @@
16321653
**
16331654
** %fossil test-wiki-render FILE [OPTIONS]
16341655
**
16351656
** Options:
16361657
** --buttons Set the WIKI_BUTTONS flag
1658
+** --htmlonly Set the WIKI_HTMLONLY flag
1659
+** --linksonly Set the WIKI_LINKSONLY flag
1660
+** --nobadlinks Set the WIKI_NOBADLINKS flag
1661
+** --inline Set the WIKI_INLINE flag
1662
+** --noblock Set the WIKI_NOBLOCK flag
16371663
*/
16381664
void test_wiki_render(void){
16391665
Blob in, out;
16401666
int flags = 0;
16411667
if( find_option("buttons",0,0)!=0 ) flags |= WIKI_BUTTONS;
1668
+ if( find_option("htmlonly",0,0)!=0 ) flags |= WIKI_HTMLONLY;
1669
+ if( find_option("linksonly",0,0)!=0 ) flags |= WIKI_LINKSONLY;
1670
+ if( find_option("nobadlinks",0,0)!=0 ) flags |= WIKI_NOBADLINKS;
1671
+ if( find_option("inline",0,0)!=0 ) flags |= WIKI_INLINE;
1672
+ if( find_option("noblock",0,0)!=0 ) flags |= WIKI_NOBLOCK;
1673
+ db_find_and_open_repository(0,0);
16421674
verify_all_options();
16431675
if( g.argc!=3 ) usage("FILE");
16441676
blob_zero(&out);
16451677
blob_read_from_file(&in, g.argv[2]);
16461678
wiki_convert(&in, &out, flags);
@@ -1695,29 +1727,29 @@
16951727
Renderer renderer;
16961728
int tokenType;
16971729
ParsedMarkup markup;
16981730
int n;
16991731
int inlineOnly;
1700
- int wikiUseHtml = 0;
1732
+ int wikiHtmlOnly = 0;
17011733
17021734
memset(&renderer, 0, sizeof(renderer));
17031735
renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH;
17041736
if( flags & WIKI_NOBLOCK ){
17051737
renderer.state |= INLINE_MARKUP_ONLY;
17061738
}
17071739
if( wikiUsesHtml() ){
1708
- renderer.state |= WIKI_USE_HTML;
1709
- wikiUseHtml = 1;
1740
+ renderer.state |= WIKI_HTMLONLY;
1741
+ wikiHtmlOnly = 1;
17101742
}
17111743
inlineOnly = (renderer.state & INLINE_MARKUP_ONLY)!=0;
17121744
if( replaceFlag ){
17131745
db_multi_exec("DELETE FROM backlink WHERE srctype=%d AND srcid=%d",
17141746
srctype, srcid);
17151747
}
17161748
17171749
while( z[0] ){
1718
- if( wikiUseHtml ){
1750
+ if( wikiHtmlOnly ){
17191751
n = nextRawToken(z, &renderer, &tokenType);
17201752
}else{
17211753
n = nextWikiToken(z, &renderer, &tokenType);
17221754
}
17231755
switch( tokenType ){
17241756
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -23,17 +23,16 @@
23
24 #if INTERFACE
25 /*
26 ** Allowed wiki transformation operations
27 */
28 #define WIKI_NOFOLLOW 0x001
29 #define WIKI_HTML 0x002
30 #define WIKI_INLINE 0x004 /* Do not surround with <p>..</p> */
31 #define WIKI_NOBLOCK 0x008 /* No block markup of any kind */
32 #define WIKI_BUTTONS 0x010 /* Allow sub-menu buttons */
33 #define WIKI_NOBADLINKS 0x020 /* Ignore broken hyperlinks */
34 #define WIKI_DECORATEONLY 0x040 /* No markup. Only decorate links */
35 #endif
36
37
38 /*
39 ** These are the only markup attributes allowed.
@@ -386,14 +385,14 @@
386 ** State flags. Save the lower 16 bits for the WIKI_* flags.
387 */
388 #define AT_NEWLINE 0x0010000 /* At start of a line */
389 #define AT_PARAGRAPH 0x0020000 /* At start of a paragraph */
390 #define ALLOW_WIKI 0x0040000 /* Allow wiki markup */
391 #define FONT_MARKUP_ONLY 0x0080000 /* Only allow MUTYPE_FONT markup */
392 #define INLINE_MARKUP_ONLY 0x0100000 /* Allow only "inline" markup */
393 #define IN_LIST 0x0200000 /* Within wiki <ul> or <ol> */
394 #define WIKI_USE_HTML 0x0400000 /* wiki-use-html option = on */
395
396 /*
397 ** Current state of the rendering engine
398 */
399 typedef struct Renderer Renderer;
@@ -487,18 +486,27 @@
487 ** <
488 ** &
489 ** \n
490 ** [
491 **
492 ** The "[" and "\n" are only considered interesting if the "useWiki"
493 ** flag is set.
494 */
495 static int textLength(const char *z, int useWiki){
496 int n = 0;
497 int c;
498 while( (c = z[0])!=0 && c!='<' && c!='&' &&
499 (useWiki==0 || (c!='[' && c!='\n')) ){
 
 
 
 
 
 
 
 
 
500 n++;
501 z++;
502 }
503 return n;
504 }
@@ -671,13 +679,16 @@
671 }
672 if( z[0]=='[' && (n = linkLength(z))>0 ){
673 *pTokenType = TOKEN_LINK;
674 return n;
675 }
 
 
 
676 }
677 *pTokenType = TOKEN_TEXT;
678 return 1 + textLength(z+1, p->state & ALLOW_WIKI);
679 }
680
681 /*
682 ** Parse only Wiki links, return everything else as TOKEN_RAW.
683 **
@@ -985,10 +996,11 @@
985 /*
986 ** Begin a new paragraph if that something that is needed.
987 */
988 static void startAutoParagraph(Renderer *p){
989 if( p->wantAutoParagraph==0 ) return;
 
990 if( p->wikiList==MARKUP_OL || p->wikiList==MARKUP_UL ) return;
991 blob_appendf(p->pOut, "<p>", -1);
992 pushStack(p, MARKUP_P);
993 p->wantAutoParagraph = 0;
994 p->inAutoParagraph = 1;
@@ -1084,18 +1096,20 @@
1084 /*
1085 ** Return a pointer to the name part of zTarget (skipping the "wiki:" prefix
1086 ** if there is one) if zTarget is a valid wiki page name. Return NULL if
1087 ** zTarget names a page that does not exist.
1088 */
1089 static const char *validWikiPageName(const char *zTarget){
1090 if( strncmp(zTarget, "wiki:", 5)==0
1091 && wiki_name_is_wellformed((const unsigned char*)zTarget) ){
1092 return zTarget+5;
1093 }
1094 if( strcmp(zTarget, "Sandbox")==0 ) return zTarget;
1095 if( wiki_name_is_wellformed((const unsigned char *)zTarget)
1096 && db_exists("SELECT 1 FROM tag WHERE tagname GLOB 'wiki-%q'", zTarget) ){
 
 
1097 return zTarget;
1098 }
1099 return 0;
1100 }
1101
@@ -1145,19 +1159,14 @@
1145 || strncmp(zTarget, "https:", 6)==0
1146 || strncmp(zTarget, "ftp:", 4)==0
1147 || strncmp(zTarget, "mailto:", 7)==0
1148 ){
1149 blob_appendf(p->pOut, "<a href=\"%s\">", zTarget);
1150 /* zTerm = "&#x27FE;</a>"; // doesn't work on windows */
1151 }else if( zTarget[0]=='/' ){
1152 blob_appendf(p->pOut, "<a href=\"%s%h\">", g.zTop, zTarget);
1153 }else if( zTarget[0]=='.' || zTarget[0]=='#' ){
1154 if( 1 ){
1155 blob_appendf(p->pOut, "<a href=\"%h\">", zTarget);
1156 }else{
1157 zTerm = "";
1158 }
1159 }else if( is_valid_uuid(zTarget) ){
1160 int isClosed = 0;
1161 if( is_ticket(zTarget, &isClosed) ){
1162 /* Special display processing for tickets. Display the hyperlink
1163 ** as crossed out if the ticket is closed.
@@ -1181,11 +1190,11 @@
1181 blob_appendf(p->pOut, "[");
1182 zTerm = "]";
1183 }
1184 }
1185 }else if( !in_this_repo(zTarget) ){
1186 if( (p->state & (WIKI_DECORATEONLY|WIKI_NOBADLINKS))!=0 ){
1187 zTerm = "";
1188 }else{
1189 blob_appendf(p->pOut, "<span class=\"brokenlink\">[", zTarget);
1190 zTerm = "]</span>";
1191 }
@@ -1194,16 +1203,16 @@
1194 zTerm = "]</a>";
1195 }
1196 }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-'
1197 && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){
1198 blob_appendf(p->pOut, "<a href=\"%R/timeline?c=%T\">", zTarget);
1199 }else if( (z = validWikiPageName(zTarget))!=0 ){
1200 blob_appendf(p->pOut, "<a href=\"%R/wiki?name=%T\">", z);
1201 }else if( zTarget>=&zOrig[2] && !fossil_isspace(zTarget[-2]) ){
1202 /* Probably an array subscript in code */
1203 zTerm = "";
1204 }else if( (p->state & (WIKI_NOBADLINKS|WIKI_DECORATEONLY))!=0 ){
1205 zTerm = "";
1206 }else{
1207 blob_appendf(p->pOut, "<span class=\"brokenlink\">[%h]", zTarget);
1208 zTerm = "</span>";
1209 }
@@ -1243,19 +1252,20 @@
1243 static void wiki_render(Renderer *p, char *z){
1244 int tokenType;
1245 ParsedMarkup markup;
1246 int n;
1247 int inlineOnly = (p->state & INLINE_MARKUP_ONLY)!=0;
1248 int wikiUseHtml = (p->state & WIKI_USE_HTML)!=0;
 
1249 char *zOrig = z;
1250
1251 /* Make sure the attribute constants and names still align
1252 ** following changes in the attribute list. */
1253 assert( fossil_strcmp(aAttribute[ATTR_WIDTH].zName, "width")==0 );
1254
1255 while( z[0] ){
1256 if( wikiUseHtml ){
1257 n = nextRawToken(z, p, &tokenType);
1258 }else{
1259 n = nextWikiToken(z, p, &tokenType);
1260 }
1261 p->state &= ~(AT_NEWLINE|AT_PARAGRAPH);
@@ -1372,33 +1382,37 @@
1372 startAutoParagraph(p);
1373 zTarget = &z[1];
1374 for(i=1; z[i] && z[i]!=']'; i++){
1375 if( z[i]=='|' && zDisplay==0 ){
1376 zDisplay = &z[i+1];
1377 iS1 = i;
1378 cS1 = z[i];
1379 z[i] = 0;
1380 for(j=i-1; j>0 && fossil_isspace(z[j]); j--){ z[j] = 0; }
1381 }
1382 }
1383 z[i] = 0;
1384 if( zDisplay==0 ){
1385 zDisplay = zTarget;
1386 }else{
1387 while( fossil_isspace(*zDisplay) ) zDisplay++;
1388 }
1389 openHyperlink(p, zTarget, zClose, sizeof(zClose), zOrig);
1390 if( zClose[0] ){
 
 
 
 
 
 
 
1391 savedState = p->state;
1392 p->state &= ~ALLOW_WIKI;
1393 p->state |= FONT_MARKUP_ONLY;
1394 wiki_render(p, zDisplay);
1395 p->state = savedState;
1396 blob_append(p->pOut, zClose, -1);
1397 }else{
1398 if( cS1 ) z[iS1] = cS1;
1399 blob_appendf(p->pOut, "[%h]", zTarget);
1400 }
1401 break;
1402 }
1403 case TOKEN_TEXT: {
1404 int i;
@@ -1406,11 +1420,15 @@
1406 if( i<n ) startAutoParagraph(p);
1407 blob_append(p->pOut, z, n);
1408 break;
1409 }
1410 case TOKEN_RAW: {
1411 blob_append(p->pOut, z, n);
 
 
 
 
1412 break;
1413 }
1414 case TOKEN_MARKUP: {
1415 const char *zId;
1416 int iDiv;
@@ -1501,22 +1519,26 @@
1501 /* Enter <verbatim> processing. With verbatim enabled, all other
1502 ** markup other than the corresponding end-tag with the same ID is
1503 ** ignored.
1504 */
1505 if( markup.iCode==MARKUP_VERBATIM ){
1506 int vAttrIdx, vAttrDidAppend=0;
1507 p->zVerbatimId = 0;
1508 p->inVerbatim = 1;
1509 p->preVerbState = p->state;
1510 p->state &= ~ALLOW_WIKI;
1511 for (vAttrIdx = 0; vAttrIdx < markup.nAttr; vAttrIdx++){
1512 if( markup.aAttr[vAttrIdx].iACode == ATTR_ID ){
1513 p->zVerbatimId = markup.aAttr[0].zValue;
1514 }else if( markup.aAttr[vAttrIdx].iACode == ATTR_TYPE ){
1515 blob_appendf(p->pOut, "<pre name='code' class='%s'>",
1516 markup.aAttr[vAttrIdx].zValue);
1517 vAttrDidAppend=1;
 
 
 
 
1518 }
1519 }
1520 if( !vAttrDidAppend ) {
1521 endAutoParagraph(p);
1522 blob_append(p->pOut, "<pre class='verbatim'>",-1);
@@ -1592,13 +1614,10 @@
1592 ** reply.
1593 */
1594 void wiki_convert(Blob *pIn, Blob *pOut, int flags){
1595 Renderer renderer;
1596
1597 /* Never show bad hyperlinks */
1598 flags |= WIKI_NOBADLINKS;
1599
1600 memset(&renderer, 0, sizeof(renderer));
1601 renderer.renderFlags = flags;
1602 renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH|flags;
1603 if( flags & WIKI_NOBLOCK ){
1604 renderer.state |= INLINE_MARKUP_ONLY;
@@ -1607,24 +1626,26 @@
1607 renderer.wantAutoParagraph = 0;
1608 }else{
1609 renderer.wantAutoParagraph = 1;
1610 }
1611 if( wikiUsesHtml() ){
1612 renderer.state |= WIKI_USE_HTML;
1613 }
1614 if( pOut ){
1615 renderer.pOut = pOut;
1616 }else{
1617 renderer.pOut = cgi_output_blob();
1618 }
1619
1620 blob_strip_bom(pIn, 0);
 
1621 wiki_render(&renderer, blob_str(pIn));
1622 endAutoParagraph(&renderer);
1623 while( renderer.nStack ){
1624 popStack(&renderer);
1625 }
 
1626 blob_append(renderer.pOut, "\n", 1);
1627 free(renderer.aStack);
1628 }
1629
1630 /*
@@ -1632,15 +1653,26 @@
1632 **
1633 ** %fossil test-wiki-render FILE [OPTIONS]
1634 **
1635 ** Options:
1636 ** --buttons Set the WIKI_BUTTONS flag
 
 
 
 
 
1637 */
1638 void test_wiki_render(void){
1639 Blob in, out;
1640 int flags = 0;
1641 if( find_option("buttons",0,0)!=0 ) flags |= WIKI_BUTTONS;
 
 
 
 
 
 
1642 verify_all_options();
1643 if( g.argc!=3 ) usage("FILE");
1644 blob_zero(&out);
1645 blob_read_from_file(&in, g.argv[2]);
1646 wiki_convert(&in, &out, flags);
@@ -1695,29 +1727,29 @@
1695 Renderer renderer;
1696 int tokenType;
1697 ParsedMarkup markup;
1698 int n;
1699 int inlineOnly;
1700 int wikiUseHtml = 0;
1701
1702 memset(&renderer, 0, sizeof(renderer));
1703 renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH;
1704 if( flags & WIKI_NOBLOCK ){
1705 renderer.state |= INLINE_MARKUP_ONLY;
1706 }
1707 if( wikiUsesHtml() ){
1708 renderer.state |= WIKI_USE_HTML;
1709 wikiUseHtml = 1;
1710 }
1711 inlineOnly = (renderer.state & INLINE_MARKUP_ONLY)!=0;
1712 if( replaceFlag ){
1713 db_multi_exec("DELETE FROM backlink WHERE srctype=%d AND srcid=%d",
1714 srctype, srcid);
1715 }
1716
1717 while( z[0] ){
1718 if( wikiUseHtml ){
1719 n = nextRawToken(z, &renderer, &tokenType);
1720 }else{
1721 n = nextWikiToken(z, &renderer, &tokenType);
1722 }
1723 switch( tokenType ){
1724
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -23,17 +23,16 @@
23
24 #if INTERFACE
25 /*
26 ** Allowed wiki transformation operations
27 */
28 #define WIKI_HTMLONLY 0x001 /* HTML markup only. No wiki */
29 #define WIKI_INLINE 0x002 /* Do not surround with <p>..</p> */
30 #define WIKI_NOBLOCK 0x004 /* No block markup of any kind */
31 #define WIKI_BUTTONS 0x008 /* Allow sub-menu buttons */
32 #define WIKI_NOBADLINKS 0x010 /* Ignore broken hyperlinks */
33 #define WIKI_LINKSONLY 0x020 /* No markup. Only decorate links */
 
34 #endif
35
36
37 /*
38 ** These are the only markup attributes allowed.
@@ -386,14 +385,14 @@
385 ** State flags. Save the lower 16 bits for the WIKI_* flags.
386 */
387 #define AT_NEWLINE 0x0010000 /* At start of a line */
388 #define AT_PARAGRAPH 0x0020000 /* At start of a paragraph */
389 #define ALLOW_WIKI 0x0040000 /* Allow wiki markup */
390 #define ALLOW_LINKS 0x0080000 /* Allow [...] hyperlinks */
391 #define FONT_MARKUP_ONLY 0x0100000 /* Only allow MUTYPE_FONT markup */
392 #define INLINE_MARKUP_ONLY 0x0200000 /* Allow only "inline" markup */
393 #define IN_LIST 0x0400000 /* Within wiki <ul> or <ol> */
394
395 /*
396 ** Current state of the rendering engine
397 */
398 typedef struct Renderer Renderer;
@@ -487,18 +486,27 @@
486 ** <
487 ** &
488 ** \n
489 ** [
490 **
491 ** The "[" is only considered if flags contain ALLOW_LINKS or ALLOW_WIKI.
492 ** The "\n" is only considered interesting if the flags constains ALLOW_WIKI.
493 */
494 static int textLength(const char *z, int flags){
495 int n = 0;
496 int c, x1, x2;
497
498 if( flags & ALLOW_WIKI ){
499 x1 = '[';
500 x2 = '\n';
501 }else if( flags & ALLOW_LINKS ){
502 x1 = '[';
503 x2 = 0;
504 }else{
505 x1 = x2 = 0;
506 }
507 while( (c = z[0])!=0 && c!='<' && c!='&' && c!=x1 && c!=x2 ){
508 n++;
509 z++;
510 }
511 return n;
512 }
@@ -671,13 +679,16 @@
679 }
680 if( z[0]=='[' && (n = linkLength(z))>0 ){
681 *pTokenType = TOKEN_LINK;
682 return n;
683 }
684 }else if( (p->state & ALLOW_LINKS)!=0 && z[0]=='[' && (n = linkLength(z))>0 ){
685 *pTokenType = TOKEN_LINK;
686 return n;
687 }
688 *pTokenType = TOKEN_TEXT;
689 return 1 + textLength(z+1, p->state);
690 }
691
692 /*
693 ** Parse only Wiki links, return everything else as TOKEN_RAW.
694 **
@@ -985,10 +996,11 @@
996 /*
997 ** Begin a new paragraph if that something that is needed.
998 */
999 static void startAutoParagraph(Renderer *p){
1000 if( p->wantAutoParagraph==0 ) return;
1001 if( p->state & WIKI_LINKSONLY ) return;
1002 if( p->wikiList==MARKUP_OL || p->wikiList==MARKUP_UL ) return;
1003 blob_appendf(p->pOut, "<p>", -1);
1004 pushStack(p, MARKUP_P);
1005 p->wantAutoParagraph = 0;
1006 p->inAutoParagraph = 1;
@@ -1084,18 +1096,20 @@
1096 /*
1097 ** Return a pointer to the name part of zTarget (skipping the "wiki:" prefix
1098 ** if there is one) if zTarget is a valid wiki page name. Return NULL if
1099 ** zTarget names a page that does not exist.
1100 */
1101 static const char *validWikiPageName(Renderer *p, const char *zTarget){
1102 if( strncmp(zTarget, "wiki:", 5)==0
1103 && wiki_name_is_wellformed((const unsigned char*)zTarget) ){
1104 return zTarget+5;
1105 }
1106 if( strcmp(zTarget, "Sandbox")==0 ) return zTarget;
1107 if( wiki_name_is_wellformed((const unsigned char *)zTarget)
1108 && ((p->state & WIKI_NOBADLINKS)==0 ||
1109 db_exists("SELECT 1 FROM tag WHERE tagname GLOB 'wiki-%q'", zTarget))
1110 ){
1111 return zTarget;
1112 }
1113 return 0;
1114 }
1115
@@ -1145,19 +1159,14 @@
1159 || strncmp(zTarget, "https:", 6)==0
1160 || strncmp(zTarget, "ftp:", 4)==0
1161 || strncmp(zTarget, "mailto:", 7)==0
1162 ){
1163 blob_appendf(p->pOut, "<a href=\"%s\">", zTarget);
 
1164 }else if( zTarget[0]=='/' ){
1165 blob_appendf(p->pOut, "<a href=\"%s%h\">", g.zTop, zTarget);
1166 }else if( zTarget[0]=='.' || zTarget[0]=='#' ){
1167 blob_appendf(p->pOut, "<a href=\"%h\">", zTarget);
 
 
 
 
1168 }else if( is_valid_uuid(zTarget) ){
1169 int isClosed = 0;
1170 if( is_ticket(zTarget, &isClosed) ){
1171 /* Special display processing for tickets. Display the hyperlink
1172 ** as crossed out if the ticket is closed.
@@ -1181,11 +1190,11 @@
1190 blob_appendf(p->pOut, "[");
1191 zTerm = "]";
1192 }
1193 }
1194 }else if( !in_this_repo(zTarget) ){
1195 if( (p->state & (WIKI_LINKSONLY|WIKI_NOBADLINKS))!=0 ){
1196 zTerm = "";
1197 }else{
1198 blob_appendf(p->pOut, "<span class=\"brokenlink\">[", zTarget);
1199 zTerm = "]</span>";
1200 }
@@ -1194,16 +1203,16 @@
1203 zTerm = "]</a>";
1204 }
1205 }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-'
1206 && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){
1207 blob_appendf(p->pOut, "<a href=\"%R/timeline?c=%T\">", zTarget);
1208 }else if( (z = validWikiPageName(p, zTarget))!=0 ){
1209 blob_appendf(p->pOut, "<a href=\"%R/wiki?name=%T\">", z);
1210 }else if( zTarget>=&zOrig[2] && !fossil_isspace(zTarget[-2]) ){
1211 /* Probably an array subscript in code */
1212 zTerm = "";
1213 }else if( (p->state & (WIKI_NOBADLINKS|WIKI_LINKSONLY))!=0 ){
1214 zTerm = "";
1215 }else{
1216 blob_appendf(p->pOut, "<span class=\"brokenlink\">[%h]", zTarget);
1217 zTerm = "</span>";
1218 }
@@ -1243,19 +1252,20 @@
1252 static void wiki_render(Renderer *p, char *z){
1253 int tokenType;
1254 ParsedMarkup markup;
1255 int n;
1256 int inlineOnly = (p->state & INLINE_MARKUP_ONLY)!=0;
1257 int wikiHtmlOnly = (p->state & (WIKI_HTMLONLY | WIKI_LINKSONLY))!=0;
1258 int linksOnly = (p->state & WIKI_LINKSONLY)!=0;
1259 char *zOrig = z;
1260
1261 /* Make sure the attribute constants and names still align
1262 ** following changes in the attribute list. */
1263 assert( fossil_strcmp(aAttribute[ATTR_WIDTH].zName, "width")==0 );
1264
1265 while( z[0] ){
1266 if( wikiHtmlOnly ){
1267 n = nextRawToken(z, p, &tokenType);
1268 }else{
1269 n = nextWikiToken(z, p, &tokenType);
1270 }
1271 p->state &= ~(AT_NEWLINE|AT_PARAGRAPH);
@@ -1372,33 +1382,37 @@
1382 startAutoParagraph(p);
1383 zTarget = &z[1];
1384 for(i=1; z[i] && z[i]!=']'; i++){
1385 if( z[i]=='|' && zDisplay==0 ){
1386 zDisplay = &z[i+1];
1387 for(j=i; j>0 && fossil_isspace(z[j-1]); j--){}
1388 iS1 = j;
1389 cS1 = z[j];
1390 z[j] = 0;
1391 }
1392 }
1393 z[i] = 0;
1394 if( zDisplay==0 ){
1395 zDisplay = zTarget;
1396 }else{
1397 while( fossil_isspace(*zDisplay) ) zDisplay++;
1398 }
1399 openHyperlink(p, zTarget, zClose, sizeof(zClose), zOrig);
1400 if( linksOnly || zClose[0]==0 || p->inVerbatim ){
1401 if( cS1 ) z[iS1] = cS1;
1402 if( zClose[0]!=']' ){
1403 blob_appendf(p->pOut, "[%h]%s", zTarget, zClose);
1404 }else{
1405 blob_appendf(p->pOut, "%h%s", zTarget, zClose);
1406 }
1407 }else{
1408 savedState = p->state;
1409 p->state &= ~ALLOW_WIKI;
1410 p->state |= FONT_MARKUP_ONLY;
1411 wiki_render(p, zDisplay);
1412 p->state = savedState;
1413 blob_append(p->pOut, zClose, -1);
 
 
 
1414 }
1415 break;
1416 }
1417 case TOKEN_TEXT: {
1418 int i;
@@ -1406,11 +1420,15 @@
1420 if( i<n ) startAutoParagraph(p);
1421 blob_append(p->pOut, z, n);
1422 break;
1423 }
1424 case TOKEN_RAW: {
1425 if( linksOnly ){
1426 htmlize_to_blob(p->pOut, z, n);
1427 }else{
1428 blob_append(p->pOut, z, n);
1429 }
1430 break;
1431 }
1432 case TOKEN_MARKUP: {
1433 const char *zId;
1434 int iDiv;
@@ -1501,22 +1519,26 @@
1519 /* Enter <verbatim> processing. With verbatim enabled, all other
1520 ** markup other than the corresponding end-tag with the same ID is
1521 ** ignored.
1522 */
1523 if( markup.iCode==MARKUP_VERBATIM ){
1524 int ii, vAttrDidAppend=0;
1525 p->zVerbatimId = 0;
1526 p->inVerbatim = 1;
1527 p->preVerbState = p->state;
1528 p->state &= ~ALLOW_WIKI;
1529 for(ii=0; ii<markup.nAttr; ii++){
1530 if( markup.aAttr[ii].iACode == ATTR_ID ){
1531 p->zVerbatimId = markup.aAttr[ii].zValue;
1532 }else if( markup.aAttr[ii].iACode == ATTR_TYPE ){
1533 if( fossil_stricmp(markup.aAttr[ii].zValue, "allow-links")==0 ){
1534 p->state |= ALLOW_LINKS;
1535 }else{
1536 blob_appendf(p->pOut, "<pre name='code' class='%s'>",
1537 markup.aAttr[ii].zValue);
1538 vAttrDidAppend=1;
1539 }
1540 }
1541 }
1542 if( !vAttrDidAppend ) {
1543 endAutoParagraph(p);
1544 blob_append(p->pOut, "<pre class='verbatim'>",-1);
@@ -1592,13 +1614,10 @@
1614 ** reply.
1615 */
1616 void wiki_convert(Blob *pIn, Blob *pOut, int flags){
1617 Renderer renderer;
1618
 
 
 
1619 memset(&renderer, 0, sizeof(renderer));
1620 renderer.renderFlags = flags;
1621 renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH|flags;
1622 if( flags & WIKI_NOBLOCK ){
1623 renderer.state |= INLINE_MARKUP_ONLY;
@@ -1607,24 +1626,26 @@
1626 renderer.wantAutoParagraph = 0;
1627 }else{
1628 renderer.wantAutoParagraph = 1;
1629 }
1630 if( wikiUsesHtml() ){
1631 renderer.state |= WIKI_HTMLONLY;
1632 }
1633 if( pOut ){
1634 renderer.pOut = pOut;
1635 }else{
1636 renderer.pOut = cgi_output_blob();
1637 }
1638
1639 blob_strip_bom(pIn, 0);
1640 if( flags & WIKI_LINKSONLY ) blob_append(renderer.pOut, "<pre>", 5);
1641 wiki_render(&renderer, blob_str(pIn));
1642 endAutoParagraph(&renderer);
1643 while( renderer.nStack ){
1644 popStack(&renderer);
1645 }
1646 if( flags & WIKI_LINKSONLY ) blob_append(renderer.pOut, "</pre>", 5);
1647 blob_append(renderer.pOut, "\n", 1);
1648 free(renderer.aStack);
1649 }
1650
1651 /*
@@ -1632,15 +1653,26 @@
1653 **
1654 ** %fossil test-wiki-render FILE [OPTIONS]
1655 **
1656 ** Options:
1657 ** --buttons Set the WIKI_BUTTONS flag
1658 ** --htmlonly Set the WIKI_HTMLONLY flag
1659 ** --linksonly Set the WIKI_LINKSONLY flag
1660 ** --nobadlinks Set the WIKI_NOBADLINKS flag
1661 ** --inline Set the WIKI_INLINE flag
1662 ** --noblock Set the WIKI_NOBLOCK flag
1663 */
1664 void test_wiki_render(void){
1665 Blob in, out;
1666 int flags = 0;
1667 if( find_option("buttons",0,0)!=0 ) flags |= WIKI_BUTTONS;
1668 if( find_option("htmlonly",0,0)!=0 ) flags |= WIKI_HTMLONLY;
1669 if( find_option("linksonly",0,0)!=0 ) flags |= WIKI_LINKSONLY;
1670 if( find_option("nobadlinks",0,0)!=0 ) flags |= WIKI_NOBADLINKS;
1671 if( find_option("inline",0,0)!=0 ) flags |= WIKI_INLINE;
1672 if( find_option("noblock",0,0)!=0 ) flags |= WIKI_NOBLOCK;
1673 db_find_and_open_repository(0,0);
1674 verify_all_options();
1675 if( g.argc!=3 ) usage("FILE");
1676 blob_zero(&out);
1677 blob_read_from_file(&in, g.argv[2]);
1678 wiki_convert(&in, &out, flags);
@@ -1695,29 +1727,29 @@
1727 Renderer renderer;
1728 int tokenType;
1729 ParsedMarkup markup;
1730 int n;
1731 int inlineOnly;
1732 int wikiHtmlOnly = 0;
1733
1734 memset(&renderer, 0, sizeof(renderer));
1735 renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH;
1736 if( flags & WIKI_NOBLOCK ){
1737 renderer.state |= INLINE_MARKUP_ONLY;
1738 }
1739 if( wikiUsesHtml() ){
1740 renderer.state |= WIKI_HTMLONLY;
1741 wikiHtmlOnly = 1;
1742 }
1743 inlineOnly = (renderer.state & INLINE_MARKUP_ONLY)!=0;
1744 if( replaceFlag ){
1745 db_multi_exec("DELETE FROM backlink WHERE srctype=%d AND srcid=%d",
1746 srctype, srcid);
1747 }
1748
1749 while( z[0] ){
1750 if( wikiHtmlOnly ){
1751 n = nextRawToken(z, &renderer, &tokenType);
1752 }else{
1753 n = nextWikiToken(z, &renderer, &tokenType);
1754 }
1755 switch( tokenType ){
1756
+80 -43
--- src/xfer.c
+++ src/xfer.c
@@ -1266,11 +1266,23 @@
12661266
/*
12671267
** Format strings for progress reporting.
12681268
*/
12691269
static const char zLabelFormat[] = "%-10s %10s %10s %10s %10s\n";
12701270
static const char zValueFormat[] = "\r%-10s %10d %10d %10d %10d\n";
1271
+static const char zBriefFormat[] =
1272
+ "Round-trips: %d Artifacts sent: %d received: %d\r";
12711273
1274
+#if INTERFACE
1275
+/*
1276
+** Flag options for controlling client_sync()
1277
+*/
1278
+#define SYNC_PUSH 0x0001
1279
+#define SYNC_PULL 0x0002
1280
+#define SYNC_CLONE 0x0004
1281
+#define SYNC_PRIVATE 0x0008
1282
+#define SYNC_VERBOSE 0x0010
1283
+#endif
12721284
12731285
/*
12741286
** Sync to the host identified in g.urlName and g.urlPath. This
12751287
** routine is called by the client.
12761288
**
@@ -1277,16 +1289,13 @@
12771289
** Records are pushed to the server if pushFlag is true. Records
12781290
** are pulled if pullFlag is true. A full sync occurs if both are
12791291
** true.
12801292
*/
12811293
int client_sync(
1282
- int pushFlag, /* True to do a push (or a sync) */
1283
- int pullFlag, /* True to do a pull (or a sync) */
1284
- int cloneFlag, /* True if this is a clone */
1285
- int privateFlag, /* True to exchange private branches */
1286
- int configRcvMask, /* Receive these configuration items */
1287
- int configSendMask /* Send these configuration items */
1294
+ unsigned syncFlags, /* Mask of SYNC_* flags */
1295
+ unsigned configRcvMask, /* Receive these configuration items */
1296
+ unsigned configSendMask /* Send these configuration items */
12881297
){
12891298
int go = 1; /* Loop until zero */
12901299
int nCardSent = 0; /* Number of cards sent */
12911300
int nCardRcvd = 0; /* Number of cards received */
12921301
int nCycle = 0; /* Number of round trips to the server */
@@ -1304,27 +1313,30 @@
13041313
int lastPctDone = -1; /* Last displayed pctDone */
13051314
double rArrivalTime; /* Time at which a message arrived */
13061315
const char *zSCode = db_get("server-code", "x");
13071316
const char *zPCode = db_get("project-code", 0);
13081317
int nErr = 0; /* Number of errors */
1318
+ int nRoundtrip= 0; /* Number of HTTP requests */
1319
+ int nArtifactSent = 0; /* Total artifacts sent */
1320
+ int nArtifactRcvd = 0; /* Total artifacts received */
1321
+ const char *zOpType = 0;/* Push, Pull, Sync, Clone */
13091322
1310
- if( db_get_boolean("dont-push", 0) ) pushFlag = 0;
1311
- if( pushFlag + pullFlag + cloneFlag == 0
1323
+ if( db_get_boolean("dont-push", 0) ) syncFlags &= ~SYNC_PUSH;
1324
+ if( (syncFlags & (SYNC_PUSH|SYNC_PULL|SYNC_CLONE))==0
13121325
&& configRcvMask==0 && configSendMask==0 ) return 0;
13131326
13141327
transport_stats(0, 0, 1);
13151328
socket_global_init();
13161329
memset(&xfer, 0, sizeof(xfer));
13171330
xfer.pIn = &recv;
13181331
xfer.pOut = &send;
13191332
xfer.mxSend = db_get_int("max-upload", 250000);
1320
- if( privateFlag ){
1333
+ if( syncFlags & SYNC_PRIVATE ){
13211334
g.perm.Private = 1;
13221335
xfer.syncPrivate = 1;
13231336
}
13241337
1325
- assert( pushFlag | pullFlag | cloneFlag | configRcvMask | configSendMask );
13261338
db_begin_transaction();
13271339
db_record_repository_filename(0);
13281340
db_multi_exec(
13291341
"CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
13301342
);
@@ -1335,33 +1347,39 @@
13351347
blob_zero(&xfer.line);
13361348
origConfigRcvMask = 0;
13371349
13381350
13391351
/* Send the send-private pragma if we are trying to sync private data */
1340
- if( privateFlag ) blob_append(&send, "pragma send-private\n", -1);
1352
+ if( syncFlags & SYNC_PRIVATE ){
1353
+ blob_append(&send, "pragma send-private\n", -1);
1354
+ }
13411355
13421356
/*
13431357
** Always begin with a clone, pull, or push message
13441358
*/
1345
- if( cloneFlag ){
1359
+ if( syncFlags & SYNC_CLONE ){
13461360
blob_appendf(&send, "clone 3 %d\n", cloneSeqno);
1347
- pushFlag = 0;
1348
- pullFlag = 0;
1361
+ syncFlags &= ~(SYNC_PUSH|SYNC_PULL);
13491362
nCardSent++;
13501363
/* TBD: Request all transferable configuration values */
13511364
content_enable_dephantomize(0);
1352
- }else if( pullFlag ){
1365
+ zOpType = "Clone";
1366
+ }else if( syncFlags & SYNC_PULL ){
13531367
blob_appendf(&send, "pull %s %s\n", zSCode, zPCode);
13541368
nCardSent++;
1369
+ zOpType = "Pull";
13551370
}
1356
- if( pushFlag ){
1371
+ if( syncFlags & SYNC_PUSH ){
13571372
blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
13581373
nCardSent++;
1374
+ if( (syncFlags & SYNC_PULL)==0 ) zOpType = "Push";
13591375
}
13601376
manifest_crosslink_begin();
13611377
transport_global_startup();
1362
- fossil_print(zLabelFormat, "", "Bytes", "Cards", "Artifacts", "Deltas");
1378
+ if( syncFlags & SYNC_VERBOSE ){
1379
+ fossil_print(zLabelFormat, "", "Bytes", "Cards", "Artifacts", "Deltas");
1380
+ }
13631381
13641382
while( go ){
13651383
int newPhantom = 0;
13661384
char *zRandomness;
13671385
@@ -1374,25 +1392,28 @@
13741392
}
13751393
13761394
/* Generate gimme cards for phantoms and leaf cards
13771395
** for all leaves.
13781396
*/
1379
- if( pullFlag || (cloneFlag && cloneSeqno==1) ){
1397
+ if( (syncFlags & SYNC_PULL)!=0
1398
+ || ((syncFlags & SYNC_CLONE)!=0 && cloneSeqno==1)
1399
+ ){
13801400
request_phantoms(&xfer, mxPhantomReq);
13811401
}
1382
- if( pushFlag ){
1402
+ if( syncFlags & SYNC_PUSH ){
13831403
send_unsent(&xfer);
13841404
nCardSent += send_unclustered(&xfer);
1385
- if( privateFlag ) send_private(&xfer);
1405
+ if( syncFlags & SYNC_PRIVATE ) send_private(&xfer);
13861406
}
13871407
13881408
/* Send configuration parameter requests. On a clone, delay sending
13891409
** this until the second cycle since the login card might fail on
13901410
** the first cycle.
13911411
*/
1392
- if( configRcvMask && (cloneFlag==0 || nCycle>0) ){
1412
+ if( configRcvMask && ((syncFlags & SYNC_CLONE)==0 || nCycle>0) ){
13931413
const char *zName;
1414
+ if( zOpType==0 ) zOpType = "Pull";
13941415
zName = configure_first_name(configRcvMask);
13951416
while( zName ){
13961417
blob_appendf(&send, "reqconfig %s\n", zName);
13971418
zName = configure_next_name(configRcvMask);
13981419
nCardSent++;
@@ -1407,10 +1428,11 @@
14071428
configRcvMask = 0;
14081429
}
14091430
14101431
/* Send configuration parameters being pushed */
14111432
if( configSendMask ){
1433
+ if( zOpType==0 ) zOpType = "Push";
14121434
if( configSendMask & CONFIGSET_OLDFORMAT ){
14131435
const char *zName;
14141436
zName = configure_first_name(configSendMask);
14151437
while( zName ){
14161438
send_legacy_config_card(&xfer, zName);
@@ -1430,42 +1452,50 @@
14301452
zRandomness = db_text(0, "SELECT hex(randomblob(20))");
14311453
blob_appendf(&send, "# %s\n", zRandomness);
14321454
free(zRandomness);
14331455
14341456
/* Exchange messages with the server */
1435
- fossil_print(zValueFormat, "Sent:",
1436
- blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent,
1437
- xfer.nFileSent, xfer.nDeltaSent);
1457
+ if( syncFlags & SYNC_VERBOSE ){
1458
+ fossil_print(zValueFormat, "Sent:",
1459
+ blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent,
1460
+ xfer.nFileSent, xfer.nDeltaSent);
1461
+ }else{
1462
+ nRoundtrip++;
1463
+ nArtifactSent += xfer.nFileSent + xfer.nDeltaSent;
1464
+ fossil_print(zBriefFormat, nRoundtrip, nArtifactSent, nArtifactRcvd);
1465
+ }
14381466
nCardSent = 0;
14391467
nCardRcvd = 0;
14401468
xfer.nFileSent = 0;
14411469
xfer.nDeltaSent = 0;
14421470
xfer.nGimmeSent = 0;
14431471
xfer.nIGotSent = 0;
1444
- if( !g.cgiOutput && !g.fQuiet ){
1472
+ if( syncFlags & SYNC_VERBOSE ){
14451473
fossil_print("waiting for server...");
14461474
}
14471475
fflush(stdout);
1448
- if( http_exchange(&send, &recv, cloneFlag==0 || nCycle>0) ){
1476
+ if( http_exchange(&send, &recv, (syncFlags & SYNC_CLONE)==0 || nCycle>0) ){
14491477
nErr++;
14501478
break;
14511479
}
14521480
lastPctDone = -1;
14531481
blob_reset(&send);
14541482
rArrivalTime = db_double(0.0, "SELECT julianday('now')");
14551483
14561484
/* Send the send-private pragma if we are trying to sync private data */
1457
- if( privateFlag ) blob_append(&send, "pragma send-private\n", -1);
1485
+ if( syncFlags & SYNC_PRIVATE ){
1486
+ blob_append(&send, "pragma send-private\n", -1);
1487
+ }
14581488
14591489
/* Begin constructing the next message (which might never be
14601490
** sent) by beginning with the pull or push cards
14611491
*/
1462
- if( pullFlag ){
1492
+ if( syncFlags & SYNC_PULL ){
14631493
blob_appendf(&send, "pull %s %s\n", zSCode, zPCode);
14641494
nCardSent++;
14651495
}
1466
- if( pushFlag ){
1496
+ if( syncFlags & SYNC_PUSH ){
14671497
blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
14681498
nCardSent++;
14691499
}
14701500
go = 0;
14711501
@@ -1493,11 +1523,11 @@
14931523
nCardRcvd++;
14941524
continue;
14951525
}
14961526
xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));
14971527
nCardRcvd++;
1498
- if( !g.cgiOutput && !g.fQuiet && recv.nUsed>0 ){
1528
+ if( (syncFlags & SYNC_VERBOSE)!=0 && recv.nUsed>0 ){
14991529
pctDone = (recv.iCursor*100)/recv.nUsed;
15001530
if( pctDone!=lastPctDone ){
15011531
fossil_print("\rprocessed: %d%% ", pctDone);
15021532
lastPctDone = pctDone;
15031533
fflush(stdout);
@@ -1508,20 +1538,22 @@
15081538
** file UUID DELTASRC SIZE \n CONTENT
15091539
**
15101540
** Receive a file transmitted from the server.
15111541
*/
15121542
if( blob_eq(&xfer.aToken[0],"file") ){
1513
- xfer_accept_file(&xfer, cloneFlag);
1543
+ xfer_accept_file(&xfer, (syncFlags & SYNC_CLONE)!=0);
1544
+ nArtifactRcvd++;
15141545
}else
15151546
15161547
/* cfile UUID USIZE CSIZE \n CONTENT
15171548
** cfile UUID DELTASRC USIZE CSIZE \n CONTENT
15181549
**
15191550
** Receive a compressed file transmitted from the server.
15201551
*/
15211552
if( blob_eq(&xfer.aToken[0],"cfile") ){
15221553
xfer_accept_compressed_file(&xfer);
1554
+ nArtifactRcvd++;
15231555
}else
15241556
15251557
/* gimme UUID
15261558
**
15271559
** Server is requesting a file. If the file is a manifest, assume
@@ -1530,11 +1562,11 @@
15301562
*/
15311563
if( blob_eq(&xfer.aToken[0], "gimme")
15321564
&& xfer.nToken==2
15331565
&& blob_is_uuid(&xfer.aToken[1])
15341566
){
1535
- if( pushFlag ){
1567
+ if( syncFlags & SYNC_PUSH ){
15361568
int rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
15371569
if( rid ) send_file(&xfer, rid, &xfer.aToken[1], 0);
15381570
}
15391571
}else
15401572
@@ -1559,11 +1591,11 @@
15591591
rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
15601592
if( rid>0 ){
15611593
if( !isPriv ) content_make_public(rid);
15621594
}else if( isPriv && !g.perm.Private ){
15631595
/* ignore private files */
1564
- }else if( pullFlag || cloneFlag ){
1596
+ }else if( (syncFlags & (SYNC_PULL|SYNC_CLONE))!=0 ){
15651597
rid = content_new(blob_str(&xfer.aToken[1]), isPriv);
15661598
if( rid ) newPhantom = 1;
15671599
}
15681600
remote_has(rid);
15691601
}else
@@ -1574,11 +1606,11 @@
15741606
** Should only happen in response to a clone. This message tells
15751607
** the client what product to use for the new database.
15761608
*/
15771609
if( blob_eq(&xfer.aToken[0],"push")
15781610
&& xfer.nToken==3
1579
- && cloneFlag
1611
+ && (syncFlags & SYNC_CLONE)!=0
15801612
&& blob_is_uuid(&xfer.aToken[1])
15811613
&& blob_is_uuid(&xfer.aToken[2])
15821614
){
15831615
if( blob_eq_str(&xfer.aToken[1], zSCode, -1) ){
15841616
fossil_fatal("server loop");
@@ -1604,11 +1636,12 @@
16041636
Blob content;
16051637
blob_zero(&content);
16061638
blob_extract(xfer.pIn, size, &content);
16071639
g.perm.Admin = g.perm.RdAddr = 1;
16081640
configure_receive(zName, &content, origConfigRcvMask);
1609
- nCardSent++;
1641
+ nCardRcvd++;
1642
+ nArtifactRcvd++;
16101643
blob_reset(&content);
16111644
blob_seek(xfer.pIn, 1, BLOB_SEEK_CUR);
16121645
}else
16131646
16141647
@@ -1655,12 +1688,12 @@
16551688
** to the next cycle.
16561689
*/
16571690
if( blob_eq(&xfer.aToken[0],"message") && xfer.nToken==2 ){
16581691
char *zMsg = blob_terminate(&xfer.aToken[1]);
16591692
defossilize(zMsg);
1660
- if( pushFlag && zMsg && strglob("pull only *", zMsg) ){
1661
- pushFlag = 0;
1693
+ if( (syncFlags & SYNC_PUSH) && zMsg && strglob("pull only *", zMsg) ){
1694
+ syncFlags &= ~SYNC_PUSH;
16621695
zMsg = 0;
16631696
}
16641697
fossil_print("\rServer says: %s\n", zMsg);
16651698
}else
16661699
@@ -1683,11 +1716,11 @@
16831716
** is returned in the reply before the error card, so second and
16841717
** subsequent messages should be OK. Nevertheless, we need to ignore
16851718
** the error card on the first message of a clone.
16861719
*/
16871720
if( blob_eq(&xfer.aToken[0],"error") && xfer.nToken==2 ){
1688
- if( !cloneFlag || nCycle>0 ){
1721
+ if( (syncFlags & SYNC_CLONE)==0 || nCycle>0 ){
16891722
char *zMsg = blob_terminate(&xfer.aToken[1]);
16901723
defossilize(zMsg);
16911724
if( fossil_strcmp(zMsg, "login failed")==0 ){
16921725
if( nCycle<2 ){
16931726
if( !g.dontKeepUrl ) db_unset("last-sync-pw", 0);
@@ -1727,14 +1760,16 @@
17271760
&& (configRcvMask & CONFIGSET_OLDFORMAT)!=0
17281761
){
17291762
configure_finalize_receive();
17301763
}
17311764
origConfigRcvMask = 0;
1732
- if( nCardRcvd>0 ){
1765
+ if( nCardRcvd>0 && (syncFlags & SYNC_VERBOSE) ){
17331766
fossil_print(zValueFormat, "Received:",
17341767
blob_size(&recv), nCardRcvd,
17351768
xfer.nFileRcvd, xfer.nDeltaRcvd + xfer.nDanglingFile);
1769
+ }else{
1770
+ fossil_print(zBriefFormat, nRoundtrip, nArtifactSent, nArtifactRcvd);
17361771
}
17371772
blob_reset(&recv);
17381773
nCycle++;
17391774
17401775
/* If we received one or more files on the previous exchange but
@@ -1743,11 +1778,11 @@
17431778
nFileRecv = xfer.nFileRcvd + xfer.nDeltaRcvd + xfer.nDanglingFile;
17441779
if( (nFileRecv>0 || newPhantom) && db_exists("SELECT 1 FROM phantom") ){
17451780
go = 1;
17461781
mxPhantomReq = nFileRecv*2;
17471782
if( mxPhantomReq<200 ) mxPhantomReq = 200;
1748
- }else if( cloneFlag && nFileRecv>0 ){
1783
+ }else if( (syncFlags & SYNC_CLONE)!=0 && nFileRecv>0 ){
17491784
go = 1;
17501785
}
17511786
nCardRcvd = 0;
17521787
xfer.nFileRcvd = 0;
17531788
xfer.nDeltaRcvd = 0;
@@ -1759,25 +1794,27 @@
17591794
if( xfer.nFileSent+xfer.nDeltaSent>0 ){
17601795
go = 1;
17611796
}
17621797
17631798
/* If this is a clone, the go at least two rounds */
1764
- if( cloneFlag && nCycle==1 ) go = 1;
1799
+ if( (syncFlags & SYNC_CLONE)!=0 && nCycle==1 ) go = 1;
17651800
17661801
/* Stop the cycle if the server sends a "clone_seqno 0" card and
17671802
** we have gone at least two rounds. Always go at least two rounds
17681803
** on a clone in order to be sure to retrieve the configuration
17691804
** information which is only sent on the second round.
17701805
*/
17711806
if( cloneSeqno<=0 && nCycle>1 ) go = 0;
17721807
};
17731808
transport_stats(&nSent, &nRcvd, 1);
1774
- fossil_print("Total network traffic: %lld bytes sent, %lld bytes received\n",
1775
- nSent, nRcvd);
1809
+ if( (syncFlags & SYNC_VERBOSE)==0 ) fossil_print("\n");
1810
+ fossil_print(
1811
+ "%s finished with %lld bytes sent, %lld bytes received\n",
1812
+ zOpType, nSent, nRcvd);
17761813
transport_close();
17771814
transport_global_shutdown();
17781815
db_multi_exec("DROP TABLE onremote");
17791816
manifest_crosslink_end();
17801817
content_enable_dephantomize(1);
17811818
db_end_transaction(0);
17821819
return nErr;
17831820
}
17841821
--- src/xfer.c
+++ src/xfer.c
@@ -1266,11 +1266,23 @@
1266 /*
1267 ** Format strings for progress reporting.
1268 */
1269 static const char zLabelFormat[] = "%-10s %10s %10s %10s %10s\n";
1270 static const char zValueFormat[] = "\r%-10s %10d %10d %10d %10d\n";
 
 
1271
 
 
 
 
 
 
 
 
 
 
1272
1273 /*
1274 ** Sync to the host identified in g.urlName and g.urlPath. This
1275 ** routine is called by the client.
1276 **
@@ -1277,16 +1289,13 @@
1277 ** Records are pushed to the server if pushFlag is true. Records
1278 ** are pulled if pullFlag is true. A full sync occurs if both are
1279 ** true.
1280 */
1281 int client_sync(
1282 int pushFlag, /* True to do a push (or a sync) */
1283 int pullFlag, /* True to do a pull (or a sync) */
1284 int cloneFlag, /* True if this is a clone */
1285 int privateFlag, /* True to exchange private branches */
1286 int configRcvMask, /* Receive these configuration items */
1287 int configSendMask /* Send these configuration items */
1288 ){
1289 int go = 1; /* Loop until zero */
1290 int nCardSent = 0; /* Number of cards sent */
1291 int nCardRcvd = 0; /* Number of cards received */
1292 int nCycle = 0; /* Number of round trips to the server */
@@ -1304,27 +1313,30 @@
1304 int lastPctDone = -1; /* Last displayed pctDone */
1305 double rArrivalTime; /* Time at which a message arrived */
1306 const char *zSCode = db_get("server-code", "x");
1307 const char *zPCode = db_get("project-code", 0);
1308 int nErr = 0; /* Number of errors */
 
 
 
 
1309
1310 if( db_get_boolean("dont-push", 0) ) pushFlag = 0;
1311 if( pushFlag + pullFlag + cloneFlag == 0
1312 && configRcvMask==0 && configSendMask==0 ) return 0;
1313
1314 transport_stats(0, 0, 1);
1315 socket_global_init();
1316 memset(&xfer, 0, sizeof(xfer));
1317 xfer.pIn = &recv;
1318 xfer.pOut = &send;
1319 xfer.mxSend = db_get_int("max-upload", 250000);
1320 if( privateFlag ){
1321 g.perm.Private = 1;
1322 xfer.syncPrivate = 1;
1323 }
1324
1325 assert( pushFlag | pullFlag | cloneFlag | configRcvMask | configSendMask );
1326 db_begin_transaction();
1327 db_record_repository_filename(0);
1328 db_multi_exec(
1329 "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
1330 );
@@ -1335,33 +1347,39 @@
1335 blob_zero(&xfer.line);
1336 origConfigRcvMask = 0;
1337
1338
1339 /* Send the send-private pragma if we are trying to sync private data */
1340 if( privateFlag ) blob_append(&send, "pragma send-private\n", -1);
 
 
1341
1342 /*
1343 ** Always begin with a clone, pull, or push message
1344 */
1345 if( cloneFlag ){
1346 blob_appendf(&send, "clone 3 %d\n", cloneSeqno);
1347 pushFlag = 0;
1348 pullFlag = 0;
1349 nCardSent++;
1350 /* TBD: Request all transferable configuration values */
1351 content_enable_dephantomize(0);
1352 }else if( pullFlag ){
 
1353 blob_appendf(&send, "pull %s %s\n", zSCode, zPCode);
1354 nCardSent++;
 
1355 }
1356 if( pushFlag ){
1357 blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
1358 nCardSent++;
 
1359 }
1360 manifest_crosslink_begin();
1361 transport_global_startup();
1362 fossil_print(zLabelFormat, "", "Bytes", "Cards", "Artifacts", "Deltas");
 
 
1363
1364 while( go ){
1365 int newPhantom = 0;
1366 char *zRandomness;
1367
@@ -1374,25 +1392,28 @@
1374 }
1375
1376 /* Generate gimme cards for phantoms and leaf cards
1377 ** for all leaves.
1378 */
1379 if( pullFlag || (cloneFlag && cloneSeqno==1) ){
 
 
1380 request_phantoms(&xfer, mxPhantomReq);
1381 }
1382 if( pushFlag ){
1383 send_unsent(&xfer);
1384 nCardSent += send_unclustered(&xfer);
1385 if( privateFlag ) send_private(&xfer);
1386 }
1387
1388 /* Send configuration parameter requests. On a clone, delay sending
1389 ** this until the second cycle since the login card might fail on
1390 ** the first cycle.
1391 */
1392 if( configRcvMask && (cloneFlag==0 || nCycle>0) ){
1393 const char *zName;
 
1394 zName = configure_first_name(configRcvMask);
1395 while( zName ){
1396 blob_appendf(&send, "reqconfig %s\n", zName);
1397 zName = configure_next_name(configRcvMask);
1398 nCardSent++;
@@ -1407,10 +1428,11 @@
1407 configRcvMask = 0;
1408 }
1409
1410 /* Send configuration parameters being pushed */
1411 if( configSendMask ){
 
1412 if( configSendMask & CONFIGSET_OLDFORMAT ){
1413 const char *zName;
1414 zName = configure_first_name(configSendMask);
1415 while( zName ){
1416 send_legacy_config_card(&xfer, zName);
@@ -1430,42 +1452,50 @@
1430 zRandomness = db_text(0, "SELECT hex(randomblob(20))");
1431 blob_appendf(&send, "# %s\n", zRandomness);
1432 free(zRandomness);
1433
1434 /* Exchange messages with the server */
1435 fossil_print(zValueFormat, "Sent:",
1436 blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent,
1437 xfer.nFileSent, xfer.nDeltaSent);
 
 
 
 
 
 
1438 nCardSent = 0;
1439 nCardRcvd = 0;
1440 xfer.nFileSent = 0;
1441 xfer.nDeltaSent = 0;
1442 xfer.nGimmeSent = 0;
1443 xfer.nIGotSent = 0;
1444 if( !g.cgiOutput && !g.fQuiet ){
1445 fossil_print("waiting for server...");
1446 }
1447 fflush(stdout);
1448 if( http_exchange(&send, &recv, cloneFlag==0 || nCycle>0) ){
1449 nErr++;
1450 break;
1451 }
1452 lastPctDone = -1;
1453 blob_reset(&send);
1454 rArrivalTime = db_double(0.0, "SELECT julianday('now')");
1455
1456 /* Send the send-private pragma if we are trying to sync private data */
1457 if( privateFlag ) blob_append(&send, "pragma send-private\n", -1);
 
 
1458
1459 /* Begin constructing the next message (which might never be
1460 ** sent) by beginning with the pull or push cards
1461 */
1462 if( pullFlag ){
1463 blob_appendf(&send, "pull %s %s\n", zSCode, zPCode);
1464 nCardSent++;
1465 }
1466 if( pushFlag ){
1467 blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
1468 nCardSent++;
1469 }
1470 go = 0;
1471
@@ -1493,11 +1523,11 @@
1493 nCardRcvd++;
1494 continue;
1495 }
1496 xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));
1497 nCardRcvd++;
1498 if( !g.cgiOutput && !g.fQuiet && recv.nUsed>0 ){
1499 pctDone = (recv.iCursor*100)/recv.nUsed;
1500 if( pctDone!=lastPctDone ){
1501 fossil_print("\rprocessed: %d%% ", pctDone);
1502 lastPctDone = pctDone;
1503 fflush(stdout);
@@ -1508,20 +1538,22 @@
1508 ** file UUID DELTASRC SIZE \n CONTENT
1509 **
1510 ** Receive a file transmitted from the server.
1511 */
1512 if( blob_eq(&xfer.aToken[0],"file") ){
1513 xfer_accept_file(&xfer, cloneFlag);
 
1514 }else
1515
1516 /* cfile UUID USIZE CSIZE \n CONTENT
1517 ** cfile UUID DELTASRC USIZE CSIZE \n CONTENT
1518 **
1519 ** Receive a compressed file transmitted from the server.
1520 */
1521 if( blob_eq(&xfer.aToken[0],"cfile") ){
1522 xfer_accept_compressed_file(&xfer);
 
1523 }else
1524
1525 /* gimme UUID
1526 **
1527 ** Server is requesting a file. If the file is a manifest, assume
@@ -1530,11 +1562,11 @@
1530 */
1531 if( blob_eq(&xfer.aToken[0], "gimme")
1532 && xfer.nToken==2
1533 && blob_is_uuid(&xfer.aToken[1])
1534 ){
1535 if( pushFlag ){
1536 int rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
1537 if( rid ) send_file(&xfer, rid, &xfer.aToken[1], 0);
1538 }
1539 }else
1540
@@ -1559,11 +1591,11 @@
1559 rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
1560 if( rid>0 ){
1561 if( !isPriv ) content_make_public(rid);
1562 }else if( isPriv && !g.perm.Private ){
1563 /* ignore private files */
1564 }else if( pullFlag || cloneFlag ){
1565 rid = content_new(blob_str(&xfer.aToken[1]), isPriv);
1566 if( rid ) newPhantom = 1;
1567 }
1568 remote_has(rid);
1569 }else
@@ -1574,11 +1606,11 @@
1574 ** Should only happen in response to a clone. This message tells
1575 ** the client what product to use for the new database.
1576 */
1577 if( blob_eq(&xfer.aToken[0],"push")
1578 && xfer.nToken==3
1579 && cloneFlag
1580 && blob_is_uuid(&xfer.aToken[1])
1581 && blob_is_uuid(&xfer.aToken[2])
1582 ){
1583 if( blob_eq_str(&xfer.aToken[1], zSCode, -1) ){
1584 fossil_fatal("server loop");
@@ -1604,11 +1636,12 @@
1604 Blob content;
1605 blob_zero(&content);
1606 blob_extract(xfer.pIn, size, &content);
1607 g.perm.Admin = g.perm.RdAddr = 1;
1608 configure_receive(zName, &content, origConfigRcvMask);
1609 nCardSent++;
 
1610 blob_reset(&content);
1611 blob_seek(xfer.pIn, 1, BLOB_SEEK_CUR);
1612 }else
1613
1614
@@ -1655,12 +1688,12 @@
1655 ** to the next cycle.
1656 */
1657 if( blob_eq(&xfer.aToken[0],"message") && xfer.nToken==2 ){
1658 char *zMsg = blob_terminate(&xfer.aToken[1]);
1659 defossilize(zMsg);
1660 if( pushFlag && zMsg && strglob("pull only *", zMsg) ){
1661 pushFlag = 0;
1662 zMsg = 0;
1663 }
1664 fossil_print("\rServer says: %s\n", zMsg);
1665 }else
1666
@@ -1683,11 +1716,11 @@
1683 ** is returned in the reply before the error card, so second and
1684 ** subsequent messages should be OK. Nevertheless, we need to ignore
1685 ** the error card on the first message of a clone.
1686 */
1687 if( blob_eq(&xfer.aToken[0],"error") && xfer.nToken==2 ){
1688 if( !cloneFlag || nCycle>0 ){
1689 char *zMsg = blob_terminate(&xfer.aToken[1]);
1690 defossilize(zMsg);
1691 if( fossil_strcmp(zMsg, "login failed")==0 ){
1692 if( nCycle<2 ){
1693 if( !g.dontKeepUrl ) db_unset("last-sync-pw", 0);
@@ -1727,14 +1760,16 @@
1727 && (configRcvMask & CONFIGSET_OLDFORMAT)!=0
1728 ){
1729 configure_finalize_receive();
1730 }
1731 origConfigRcvMask = 0;
1732 if( nCardRcvd>0 ){
1733 fossil_print(zValueFormat, "Received:",
1734 blob_size(&recv), nCardRcvd,
1735 xfer.nFileRcvd, xfer.nDeltaRcvd + xfer.nDanglingFile);
 
 
1736 }
1737 blob_reset(&recv);
1738 nCycle++;
1739
1740 /* If we received one or more files on the previous exchange but
@@ -1743,11 +1778,11 @@
1743 nFileRecv = xfer.nFileRcvd + xfer.nDeltaRcvd + xfer.nDanglingFile;
1744 if( (nFileRecv>0 || newPhantom) && db_exists("SELECT 1 FROM phantom") ){
1745 go = 1;
1746 mxPhantomReq = nFileRecv*2;
1747 if( mxPhantomReq<200 ) mxPhantomReq = 200;
1748 }else if( cloneFlag && nFileRecv>0 ){
1749 go = 1;
1750 }
1751 nCardRcvd = 0;
1752 xfer.nFileRcvd = 0;
1753 xfer.nDeltaRcvd = 0;
@@ -1759,25 +1794,27 @@
1759 if( xfer.nFileSent+xfer.nDeltaSent>0 ){
1760 go = 1;
1761 }
1762
1763 /* If this is a clone, the go at least two rounds */
1764 if( cloneFlag && nCycle==1 ) go = 1;
1765
1766 /* Stop the cycle if the server sends a "clone_seqno 0" card and
1767 ** we have gone at least two rounds. Always go at least two rounds
1768 ** on a clone in order to be sure to retrieve the configuration
1769 ** information which is only sent on the second round.
1770 */
1771 if( cloneSeqno<=0 && nCycle>1 ) go = 0;
1772 };
1773 transport_stats(&nSent, &nRcvd, 1);
1774 fossil_print("Total network traffic: %lld bytes sent, %lld bytes received\n",
1775 nSent, nRcvd);
 
 
1776 transport_close();
1777 transport_global_shutdown();
1778 db_multi_exec("DROP TABLE onremote");
1779 manifest_crosslink_end();
1780 content_enable_dephantomize(1);
1781 db_end_transaction(0);
1782 return nErr;
1783 }
1784
--- src/xfer.c
+++ src/xfer.c
@@ -1266,11 +1266,23 @@
1266 /*
1267 ** Format strings for progress reporting.
1268 */
1269 static const char zLabelFormat[] = "%-10s %10s %10s %10s %10s\n";
1270 static const char zValueFormat[] = "\r%-10s %10d %10d %10d %10d\n";
1271 static const char zBriefFormat[] =
1272 "Round-trips: %d Artifacts sent: %d received: %d\r";
1273
1274 #if INTERFACE
1275 /*
1276 ** Flag options for controlling client_sync()
1277 */
1278 #define SYNC_PUSH 0x0001
1279 #define SYNC_PULL 0x0002
1280 #define SYNC_CLONE 0x0004
1281 #define SYNC_PRIVATE 0x0008
1282 #define SYNC_VERBOSE 0x0010
1283 #endif
1284
1285 /*
1286 ** Sync to the host identified in g.urlName and g.urlPath. This
1287 ** routine is called by the client.
1288 **
@@ -1277,16 +1289,13 @@
1289 ** Records are pushed to the server if pushFlag is true. Records
1290 ** are pulled if pullFlag is true. A full sync occurs if both are
1291 ** true.
1292 */
1293 int client_sync(
1294 unsigned syncFlags, /* Mask of SYNC_* flags */
1295 unsigned configRcvMask, /* Receive these configuration items */
1296 unsigned configSendMask /* Send these configuration items */
 
 
 
1297 ){
1298 int go = 1; /* Loop until zero */
1299 int nCardSent = 0; /* Number of cards sent */
1300 int nCardRcvd = 0; /* Number of cards received */
1301 int nCycle = 0; /* Number of round trips to the server */
@@ -1304,27 +1313,30 @@
1313 int lastPctDone = -1; /* Last displayed pctDone */
1314 double rArrivalTime; /* Time at which a message arrived */
1315 const char *zSCode = db_get("server-code", "x");
1316 const char *zPCode = db_get("project-code", 0);
1317 int nErr = 0; /* Number of errors */
1318 int nRoundtrip= 0; /* Number of HTTP requests */
1319 int nArtifactSent = 0; /* Total artifacts sent */
1320 int nArtifactRcvd = 0; /* Total artifacts received */
1321 const char *zOpType = 0;/* Push, Pull, Sync, Clone */
1322
1323 if( db_get_boolean("dont-push", 0) ) syncFlags &= ~SYNC_PUSH;
1324 if( (syncFlags & (SYNC_PUSH|SYNC_PULL|SYNC_CLONE))==0
1325 && configRcvMask==0 && configSendMask==0 ) return 0;
1326
1327 transport_stats(0, 0, 1);
1328 socket_global_init();
1329 memset(&xfer, 0, sizeof(xfer));
1330 xfer.pIn = &recv;
1331 xfer.pOut = &send;
1332 xfer.mxSend = db_get_int("max-upload", 250000);
1333 if( syncFlags & SYNC_PRIVATE ){
1334 g.perm.Private = 1;
1335 xfer.syncPrivate = 1;
1336 }
1337
 
1338 db_begin_transaction();
1339 db_record_repository_filename(0);
1340 db_multi_exec(
1341 "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
1342 );
@@ -1335,33 +1347,39 @@
1347 blob_zero(&xfer.line);
1348 origConfigRcvMask = 0;
1349
1350
1351 /* Send the send-private pragma if we are trying to sync private data */
1352 if( syncFlags & SYNC_PRIVATE ){
1353 blob_append(&send, "pragma send-private\n", -1);
1354 }
1355
1356 /*
1357 ** Always begin with a clone, pull, or push message
1358 */
1359 if( syncFlags & SYNC_CLONE ){
1360 blob_appendf(&send, "clone 3 %d\n", cloneSeqno);
1361 syncFlags &= ~(SYNC_PUSH|SYNC_PULL);
 
1362 nCardSent++;
1363 /* TBD: Request all transferable configuration values */
1364 content_enable_dephantomize(0);
1365 zOpType = "Clone";
1366 }else if( syncFlags & SYNC_PULL ){
1367 blob_appendf(&send, "pull %s %s\n", zSCode, zPCode);
1368 nCardSent++;
1369 zOpType = "Pull";
1370 }
1371 if( syncFlags & SYNC_PUSH ){
1372 blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
1373 nCardSent++;
1374 if( (syncFlags & SYNC_PULL)==0 ) zOpType = "Push";
1375 }
1376 manifest_crosslink_begin();
1377 transport_global_startup();
1378 if( syncFlags & SYNC_VERBOSE ){
1379 fossil_print(zLabelFormat, "", "Bytes", "Cards", "Artifacts", "Deltas");
1380 }
1381
1382 while( go ){
1383 int newPhantom = 0;
1384 char *zRandomness;
1385
@@ -1374,25 +1392,28 @@
1392 }
1393
1394 /* Generate gimme cards for phantoms and leaf cards
1395 ** for all leaves.
1396 */
1397 if( (syncFlags & SYNC_PULL)!=0
1398 || ((syncFlags & SYNC_CLONE)!=0 && cloneSeqno==1)
1399 ){
1400 request_phantoms(&xfer, mxPhantomReq);
1401 }
1402 if( syncFlags & SYNC_PUSH ){
1403 send_unsent(&xfer);
1404 nCardSent += send_unclustered(&xfer);
1405 if( syncFlags & SYNC_PRIVATE ) send_private(&xfer);
1406 }
1407
1408 /* Send configuration parameter requests. On a clone, delay sending
1409 ** this until the second cycle since the login card might fail on
1410 ** the first cycle.
1411 */
1412 if( configRcvMask && ((syncFlags & SYNC_CLONE)==0 || nCycle>0) ){
1413 const char *zName;
1414 if( zOpType==0 ) zOpType = "Pull";
1415 zName = configure_first_name(configRcvMask);
1416 while( zName ){
1417 blob_appendf(&send, "reqconfig %s\n", zName);
1418 zName = configure_next_name(configRcvMask);
1419 nCardSent++;
@@ -1407,10 +1428,11 @@
1428 configRcvMask = 0;
1429 }
1430
1431 /* Send configuration parameters being pushed */
1432 if( configSendMask ){
1433 if( zOpType==0 ) zOpType = "Push";
1434 if( configSendMask & CONFIGSET_OLDFORMAT ){
1435 const char *zName;
1436 zName = configure_first_name(configSendMask);
1437 while( zName ){
1438 send_legacy_config_card(&xfer, zName);
@@ -1430,42 +1452,50 @@
1452 zRandomness = db_text(0, "SELECT hex(randomblob(20))");
1453 blob_appendf(&send, "# %s\n", zRandomness);
1454 free(zRandomness);
1455
1456 /* Exchange messages with the server */
1457 if( syncFlags & SYNC_VERBOSE ){
1458 fossil_print(zValueFormat, "Sent:",
1459 blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent,
1460 xfer.nFileSent, xfer.nDeltaSent);
1461 }else{
1462 nRoundtrip++;
1463 nArtifactSent += xfer.nFileSent + xfer.nDeltaSent;
1464 fossil_print(zBriefFormat, nRoundtrip, nArtifactSent, nArtifactRcvd);
1465 }
1466 nCardSent = 0;
1467 nCardRcvd = 0;
1468 xfer.nFileSent = 0;
1469 xfer.nDeltaSent = 0;
1470 xfer.nGimmeSent = 0;
1471 xfer.nIGotSent = 0;
1472 if( syncFlags & SYNC_VERBOSE ){
1473 fossil_print("waiting for server...");
1474 }
1475 fflush(stdout);
1476 if( http_exchange(&send, &recv, (syncFlags & SYNC_CLONE)==0 || nCycle>0) ){
1477 nErr++;
1478 break;
1479 }
1480 lastPctDone = -1;
1481 blob_reset(&send);
1482 rArrivalTime = db_double(0.0, "SELECT julianday('now')");
1483
1484 /* Send the send-private pragma if we are trying to sync private data */
1485 if( syncFlags & SYNC_PRIVATE ){
1486 blob_append(&send, "pragma send-private\n", -1);
1487 }
1488
1489 /* Begin constructing the next message (which might never be
1490 ** sent) by beginning with the pull or push cards
1491 */
1492 if( syncFlags & SYNC_PULL ){
1493 blob_appendf(&send, "pull %s %s\n", zSCode, zPCode);
1494 nCardSent++;
1495 }
1496 if( syncFlags & SYNC_PUSH ){
1497 blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
1498 nCardSent++;
1499 }
1500 go = 0;
1501
@@ -1493,11 +1523,11 @@
1523 nCardRcvd++;
1524 continue;
1525 }
1526 xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));
1527 nCardRcvd++;
1528 if( (syncFlags & SYNC_VERBOSE)!=0 && recv.nUsed>0 ){
1529 pctDone = (recv.iCursor*100)/recv.nUsed;
1530 if( pctDone!=lastPctDone ){
1531 fossil_print("\rprocessed: %d%% ", pctDone);
1532 lastPctDone = pctDone;
1533 fflush(stdout);
@@ -1508,20 +1538,22 @@
1538 ** file UUID DELTASRC SIZE \n CONTENT
1539 **
1540 ** Receive a file transmitted from the server.
1541 */
1542 if( blob_eq(&xfer.aToken[0],"file") ){
1543 xfer_accept_file(&xfer, (syncFlags & SYNC_CLONE)!=0);
1544 nArtifactRcvd++;
1545 }else
1546
1547 /* cfile UUID USIZE CSIZE \n CONTENT
1548 ** cfile UUID DELTASRC USIZE CSIZE \n CONTENT
1549 **
1550 ** Receive a compressed file transmitted from the server.
1551 */
1552 if( blob_eq(&xfer.aToken[0],"cfile") ){
1553 xfer_accept_compressed_file(&xfer);
1554 nArtifactRcvd++;
1555 }else
1556
1557 /* gimme UUID
1558 **
1559 ** Server is requesting a file. If the file is a manifest, assume
@@ -1530,11 +1562,11 @@
1562 */
1563 if( blob_eq(&xfer.aToken[0], "gimme")
1564 && xfer.nToken==2
1565 && blob_is_uuid(&xfer.aToken[1])
1566 ){
1567 if( syncFlags & SYNC_PUSH ){
1568 int rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
1569 if( rid ) send_file(&xfer, rid, &xfer.aToken[1], 0);
1570 }
1571 }else
1572
@@ -1559,11 +1591,11 @@
1591 rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
1592 if( rid>0 ){
1593 if( !isPriv ) content_make_public(rid);
1594 }else if( isPriv && !g.perm.Private ){
1595 /* ignore private files */
1596 }else if( (syncFlags & (SYNC_PULL|SYNC_CLONE))!=0 ){
1597 rid = content_new(blob_str(&xfer.aToken[1]), isPriv);
1598 if( rid ) newPhantom = 1;
1599 }
1600 remote_has(rid);
1601 }else
@@ -1574,11 +1606,11 @@
1606 ** Should only happen in response to a clone. This message tells
1607 ** the client what product to use for the new database.
1608 */
1609 if( blob_eq(&xfer.aToken[0],"push")
1610 && xfer.nToken==3
1611 && (syncFlags & SYNC_CLONE)!=0
1612 && blob_is_uuid(&xfer.aToken[1])
1613 && blob_is_uuid(&xfer.aToken[2])
1614 ){
1615 if( blob_eq_str(&xfer.aToken[1], zSCode, -1) ){
1616 fossil_fatal("server loop");
@@ -1604,11 +1636,12 @@
1636 Blob content;
1637 blob_zero(&content);
1638 blob_extract(xfer.pIn, size, &content);
1639 g.perm.Admin = g.perm.RdAddr = 1;
1640 configure_receive(zName, &content, origConfigRcvMask);
1641 nCardRcvd++;
1642 nArtifactRcvd++;
1643 blob_reset(&content);
1644 blob_seek(xfer.pIn, 1, BLOB_SEEK_CUR);
1645 }else
1646
1647
@@ -1655,12 +1688,12 @@
1688 ** to the next cycle.
1689 */
1690 if( blob_eq(&xfer.aToken[0],"message") && xfer.nToken==2 ){
1691 char *zMsg = blob_terminate(&xfer.aToken[1]);
1692 defossilize(zMsg);
1693 if( (syncFlags & SYNC_PUSH) && zMsg && strglob("pull only *", zMsg) ){
1694 syncFlags &= ~SYNC_PUSH;
1695 zMsg = 0;
1696 }
1697 fossil_print("\rServer says: %s\n", zMsg);
1698 }else
1699
@@ -1683,11 +1716,11 @@
1716 ** is returned in the reply before the error card, so second and
1717 ** subsequent messages should be OK. Nevertheless, we need to ignore
1718 ** the error card on the first message of a clone.
1719 */
1720 if( blob_eq(&xfer.aToken[0],"error") && xfer.nToken==2 ){
1721 if( (syncFlags & SYNC_CLONE)==0 || nCycle>0 ){
1722 char *zMsg = blob_terminate(&xfer.aToken[1]);
1723 defossilize(zMsg);
1724 if( fossil_strcmp(zMsg, "login failed")==0 ){
1725 if( nCycle<2 ){
1726 if( !g.dontKeepUrl ) db_unset("last-sync-pw", 0);
@@ -1727,14 +1760,16 @@
1760 && (configRcvMask & CONFIGSET_OLDFORMAT)!=0
1761 ){
1762 configure_finalize_receive();
1763 }
1764 origConfigRcvMask = 0;
1765 if( nCardRcvd>0 && (syncFlags & SYNC_VERBOSE) ){
1766 fossil_print(zValueFormat, "Received:",
1767 blob_size(&recv), nCardRcvd,
1768 xfer.nFileRcvd, xfer.nDeltaRcvd + xfer.nDanglingFile);
1769 }else{
1770 fossil_print(zBriefFormat, nRoundtrip, nArtifactSent, nArtifactRcvd);
1771 }
1772 blob_reset(&recv);
1773 nCycle++;
1774
1775 /* If we received one or more files on the previous exchange but
@@ -1743,11 +1778,11 @@
1778 nFileRecv = xfer.nFileRcvd + xfer.nDeltaRcvd + xfer.nDanglingFile;
1779 if( (nFileRecv>0 || newPhantom) && db_exists("SELECT 1 FROM phantom") ){
1780 go = 1;
1781 mxPhantomReq = nFileRecv*2;
1782 if( mxPhantomReq<200 ) mxPhantomReq = 200;
1783 }else if( (syncFlags & SYNC_CLONE)!=0 && nFileRecv>0 ){
1784 go = 1;
1785 }
1786 nCardRcvd = 0;
1787 xfer.nFileRcvd = 0;
1788 xfer.nDeltaRcvd = 0;
@@ -1759,25 +1794,27 @@
1794 if( xfer.nFileSent+xfer.nDeltaSent>0 ){
1795 go = 1;
1796 }
1797
1798 /* If this is a clone, the go at least two rounds */
1799 if( (syncFlags & SYNC_CLONE)!=0 && nCycle==1 ) go = 1;
1800
1801 /* Stop the cycle if the server sends a "clone_seqno 0" card and
1802 ** we have gone at least two rounds. Always go at least two rounds
1803 ** on a clone in order to be sure to retrieve the configuration
1804 ** information which is only sent on the second round.
1805 */
1806 if( cloneSeqno<=0 && nCycle>1 ) go = 0;
1807 };
1808 transport_stats(&nSent, &nRcvd, 1);
1809 if( (syncFlags & SYNC_VERBOSE)==0 ) fossil_print("\n");
1810 fossil_print(
1811 "%s finished with %lld bytes sent, %lld bytes received\n",
1812 zOpType, nSent, nRcvd);
1813 transport_close();
1814 transport_global_shutdown();
1815 db_multi_exec("DROP TABLE onremote");
1816 manifest_crosslink_end();
1817 content_enable_dephantomize(1);
1818 db_end_transaction(0);
1819 return nErr;
1820 }
1821
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -13,15 +13,15 @@
1313
#
1414
1515
#### Select one of MinGW, MinGW-64 (32-bit) or MinGW-w64 (64-bit) compilers.
1616
# By default, this is an empty string (i.e. use the native compiler).
1717
#
18
-PREFIX =
18
+# PREFIX =
1919
# PREFIX = mingw32-
2020
# PREFIX = i686-pc-mingw32-
2121
# PREFIX = i686-w64-mingw32-
22
-# PREFIX = x86_64-w64-mingw32-
22
+PREFIX = x86_64-w64-mingw32-
2323
2424
#### The toplevel directory of the source tree. Fossil can be built
2525
# in a directory that is separate from the source tree. Just change
2626
# the following to point from the build directory to the src/ folder.
2727
#
2828
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -13,15 +13,15 @@
13 #
14
15 #### Select one of MinGW, MinGW-64 (32-bit) or MinGW-w64 (64-bit) compilers.
16 # By default, this is an empty string (i.e. use the native compiler).
17 #
18 PREFIX =
19 # PREFIX = mingw32-
20 # PREFIX = i686-pc-mingw32-
21 # PREFIX = i686-w64-mingw32-
22 # PREFIX = x86_64-w64-mingw32-
23
24 #### The toplevel directory of the source tree. Fossil can be built
25 # in a directory that is separate from the source tree. Just change
26 # the following to point from the build directory to the src/ folder.
27 #
28
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -13,15 +13,15 @@
13 #
14
15 #### Select one of MinGW, MinGW-64 (32-bit) or MinGW-w64 (64-bit) compilers.
16 # By default, this is an empty string (i.e. use the native compiler).
17 #
18 # PREFIX =
19 # PREFIX = mingw32-
20 # PREFIX = i686-pc-mingw32-
21 # PREFIX = i686-w64-mingw32-
22 PREFIX = x86_64-w64-mingw32-
23
24 #### The toplevel directory of the source tree. Fossil can be built
25 # in a directory that is separate from the source tree. Just change
26 # the following to point from the build directory to the src/ folder.
27 #
28
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -13,15 +13,15 @@
1313
#
1414
1515
#### Select one of MinGW, MinGW-64 (32-bit) or MinGW-w64 (64-bit) compilers.
1616
# By default, this is an empty string (i.e. use the native compiler).
1717
#
18
-PREFIX =
18
+# PREFIX =
1919
# PREFIX = mingw32-
2020
# PREFIX = i686-pc-mingw32-
2121
# PREFIX = i686-w64-mingw32-
22
-# PREFIX = x86_64-w64-mingw32-
22
+PREFIX = x86_64-w64-mingw32-
2323
2424
#### The toplevel directory of the source tree. Fossil can be built
2525
# in a directory that is separate from the source tree. Just change
2626
# the following to point from the build directory to the src/ folder.
2727
#
2828
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -13,15 +13,15 @@
13 #
14
15 #### Select one of MinGW, MinGW-64 (32-bit) or MinGW-w64 (64-bit) compilers.
16 # By default, this is an empty string (i.e. use the native compiler).
17 #
18 PREFIX =
19 # PREFIX = mingw32-
20 # PREFIX = i686-pc-mingw32-
21 # PREFIX = i686-w64-mingw32-
22 # PREFIX = x86_64-w64-mingw32-
23
24 #### The toplevel directory of the source tree. Fossil can be built
25 # in a directory that is separate from the source tree. Just change
26 # the following to point from the build directory to the src/ folder.
27 #
28
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -13,15 +13,15 @@
13 #
14
15 #### Select one of MinGW, MinGW-64 (32-bit) or MinGW-w64 (64-bit) compilers.
16 # By default, this is an empty string (i.e. use the native compiler).
17 #
18 # PREFIX =
19 # PREFIX = mingw32-
20 # PREFIX = i686-pc-mingw32-
21 # PREFIX = i686-w64-mingw32-
22 PREFIX = x86_64-w64-mingw32-
23
24 #### The toplevel directory of the source tree. Fossil can be built
25 # in a directory that is separate from the source tree. Just change
26 # the following to point from the build directory to the src/ folder.
27 #
28

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button