Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Hyperlink processing for chat messages is now handled on the server side, where we have knowledge of interwiki links, wiki page names, and valid artifact hashes. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA3-256: |
822653c26952d70c3482227e73f11cb8 |
| User & Date: | drh 2020-12-24 13:44:30.140 |
Context
|
2020-12-24
| ||
| 15:10 | The ping=TCPPORT query parameter to /chat causes a call to /chat-ping on localhost and the given port whenever new chat content arrives. Can be used for notifications. ... (check-in: ebdd91b92f user: drh tags: trunk) | |
| 13:44 | Hyperlink processing for chat messages is now handled on the server side, where we have knowledge of interwiki links, wiki page names, and valid artifact hashes. ... (check-in: 822653c269 user: drh tags: trunk) | |
| 08:10 | chat: revert title to its originalv alue when the tab becomes active after having been inactive. ... (check-in: 0c0be4b763 user: stephan tags: trunk) | |
Changes
Changes to src/chat.c.
| ︙ | ︙ | |||
241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
blob_init(&b, P("file"), nByte);
db_bind_blob(&q, ":file", &b);
db_step(&q);
db_finalize(&q);
blob_reset(&b);
}
}
/*
** WEBPAGE: chat-poll
**
** The chat page generated by /chat using a XHR to this page in order
** to ask for new chat content. The "name" argument should begin with
** an integer which is the largest "msgid" that the chat page currently
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 |
blob_init(&b, P("file"), nByte);
db_bind_blob(&q, ":file", &b);
db_step(&q);
db_finalize(&q);
blob_reset(&b);
}
}
/*
** This routine receives raw (user-entered) message text and transforms
** it into HTML that is safe to insert using innerHTML.
**
** * HTML in the original text is escaped.
**
** * Hyperlinks are identified and tagged. Hyperlinks are:
**
** - Undelimited text of the form https:... or http:...
** - Any text enclosed within [...]
**
** Space to hold the returned string is obtained from fossil_malloc()
** and must be freed by the caller.
*/
static char *chat_format_to_html(const char *zMsg){
char *zSafe = mprintf("%h", zMsg);
int i, j, k;
Blob out;
char zClose[20];
blob_init(&out, 0, 0);
for(i=j=0; zSafe[i]; i++){
if( zSafe[i]=='[' ){
for(k=i+1; zSafe[k] && zSafe[k]!=']'; k++){}
if( zSafe[k]==']' ){
zSafe[k] = 0;
if( j<i ){
blob_append(&out, zSafe + j, i-j);
j = i;
}
wiki_resolve_hyperlink(&out, WIKI_NOBADLINKS|WIKI_TARGET_BLANK,
zSafe+i+1, zClose, sizeof(zClose), zSafe, 0);
zSafe[k] = ']';
j++;
blob_append(&out, zSafe + j, k - j);
blob_append(&out, zClose, -1);
i = k;
j = k+1;
continue;
}
}else if( zSafe[i]=='h'
&& (strncmp(zSafe+i,"http:",5)==0
|| strncmp(zSafe+i,"https:",6)==0) ){
for(k=i+1; zSafe[k] && !fossil_isspace(zSafe[k]); k++){}
if( zSafe[k] && k>i+7 ){
char c = zSafe[k];
if( !fossil_isalnum(zSafe[k-1]) && zSafe[k-1]!='/' ){
k--;
c = zSafe[k];
}
if( j<i ){
blob_append(&out, zSafe + j, i-j);
j = i;
}
zSafe[k] = 0;
wiki_resolve_hyperlink(&out, WIKI_NOBADLINKS|WIKI_TARGET_BLANK,
zSafe+i, zClose, sizeof(zClose), zSafe, 0);
zSafe[k] = c;
blob_append(&out, zSafe + j, k - j);
blob_append(&out, zClose, -1);
i = j = k;
continue;
}
}
}
if( j<i ){
blob_append(&out, zSafe+j, j-i);
}
fossil_free(zSafe);
return blob_str(&out);
}
/*
** COMMAND: test-chat-formatter
**
** Usage: %fossil test-chat-formatter STRING ...
**
** Transform each argument string into HTML that will display the
** chat message. This is used to test the formatter and to verify
** that a malicious message text will not cause HTML or JS injection
** into the chat display in a browser.
*/
void chat_test_formatter_cmd(void){
int i;
char *zOut;
db_find_and_open_repository(0,0);
g.perm.Hyperlink = 1;
for(i=0; i<g.argc; i++){
zOut = chat_format_to_html(g.argv[i]);
fossil_print("[%d]: %s\n", i, zOut);
fossil_free(zOut);
}
}
/*
** WEBPAGE: chat-poll
**
** The chat page generated by /chat using a XHR to this page in order
** to ask for new chat content. The "name" argument should begin with
** an integer which is the largest "msgid" that the chat page currently
|
| ︙ | ︙ | |||
334 335 336 337 338 339 340 |
cnt++;
blob_append(&json, zSep, -1);
zSep = ",\n";
blob_appendf(&json, "{\"msgid\":%d,\"mtime\":%!j,", id, zDate);
blob_appendf(&json, "\"xfrom\":%!j,", zFrom);
blob_appendf(&json, "\"uclr\":%!j,", hash_color(zFrom));
| < < < | | 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 |
cnt++;
blob_append(&json, zSep, -1);
zSep = ",\n";
blob_appendf(&json, "{\"msgid\":%d,\"mtime\":%!j,", id, zDate);
blob_appendf(&json, "\"xfrom\":%!j,", zFrom);
blob_appendf(&json, "\"uclr\":%!j,", hash_color(zFrom));
zMsg = chat_format_to_html(zRawMsg ? zRawMsg : "");
blob_appendf(&json, "\"xmsg\":%!j,", zMsg);
fossil_free(zMsg);
if( nByte==0 ){
blob_appendf(&json, "\"fsize\":0");
}else{
blob_appendf(&json, "\"fsize\":%d,\"fname\":%!j,\"fmime\":%!j",
|
| ︙ | ︙ |
Changes to src/chat.js.
| ︙ | ︙ | |||
279 280 281 282 283 284 285 |
// truncation off the right edge of the page.
const pRect = f.popup.e.getBoundingClientRect();
x -= pRect.width/3*2;
}
f.popup.show(x, y);
};
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 279 280 281 282 283 284 285 286 287 288 289 290 291 292 |
// truncation off the right edge of the page.
const pRect = f.popup.e.getBoundingClientRect();
x -= pRect.width/3*2;
}
f.popup.show(x, y);
};
/** Callback for poll() to inject new content into the page. */
function newcontent(jx){
var i;
if('visible'===document.visibilityState){
if(Chat.changesSincePageHidden){
Chat.changesSincePageHidden = 0;
Chat.pageTitle.innerText = Chat.pageTitleOrig;
|
| ︙ | ︙ | |||
421 422 423 424 425 426 427 |
));
}
const br = D.br();
br.style.clear = "both";
eContent.appendChild(br);
}
if(m.xmsg){
| > > > > > > > > > | | 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 |
));
}
const br = D.br();
br.style.clear = "both";
eContent.appendChild(br);
}
if(m.xmsg){
// The m.xmsg text comes from the same server as this script and
// is guaranteed by that server to be "safe" HTML - safe in the
// sense that it is not possible for a malefactor to inject HTML
// or javascript or CSS. The m.xmsg content might contain
// hyperlinks, but otherwise it will be markup-free. See the
// chat_format_to_html() routine in the server for details.
//
// Hence, even though innerHTML is normally frowned upon, it is
// perfectly safe to use in this context.
eContent.innerHTML += m.xmsg
}
eContent.classList.add('chat-message');
}
}
async function poll(){
if(poll.running) return;
poll.running = true;
|
| ︙ | ︙ |
Changes to src/wikiformat.c.
| ︙ | ︙ | |||
30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
#define WIKI_NOBLOCK 0x004 /* No block markup of any kind */
#define WIKI_BUTTONS 0x008 /* Allow sub-menu buttons */
#define WIKI_NOBADLINKS 0x010 /* Ignore broken hyperlinks */
#define WIKI_LINKSONLY 0x020 /* No markup. Only decorate links */
#define WIKI_NEWLINE 0x040 /* Honor \n - break lines at each \n */
#define WIKI_MARKDOWNLINKS 0x080 /* Resolve hyperlinks as in markdown */
#define WIKI_SAFE 0x100 /* Make the result safe for embedding */
#endif
/*
** These are the only markup attributes allowed.
*/
enum allowed_attr_t {
| > | 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
#define WIKI_NOBLOCK 0x004 /* No block markup of any kind */
#define WIKI_BUTTONS 0x008 /* Allow sub-menu buttons */
#define WIKI_NOBADLINKS 0x010 /* Ignore broken hyperlinks */
#define WIKI_LINKSONLY 0x020 /* No markup. Only decorate links */
#define WIKI_NEWLINE 0x040 /* Honor \n - break lines at each \n */
#define WIKI_MARKDOWNLINKS 0x080 /* Resolve hyperlinks as in markdown */
#define WIKI_SAFE 0x100 /* Make the result safe for embedding */
#define WIKI_TARGET_BLANK 0x200 /* Hyperlinks go to a new window */
#endif
/*
** These are the only markup attributes allowed.
*/
enum allowed_attr_t {
|
| ︙ | ︙ | |||
1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 |
const char *z;
char *zExtra = 0;
const char *zExtraNS = 0;
char *zRemote = 0;
if( zTitle ){
zExtra = mprintf(" title='%h'", zTitle);
zExtraNS = zExtra+1;
}
assert( nClose>=20 );
if( strncmp(zTarget, "http:", 5)==0
|| strncmp(zTarget, "https:", 6)==0
|| strncmp(zTarget, "ftp:", 4)==0
|| strncmp(zTarget, "mailto:", 7)==0
| > > > | 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 |
const char *z;
char *zExtra = 0;
const char *zExtraNS = 0;
char *zRemote = 0;
if( zTitle ){
zExtra = mprintf(" title='%h'", zTitle);
zExtraNS = zExtra+1;
}else if( mFlags & WIKI_TARGET_BLANK ){
zExtra = mprintf(" target='_blank'");
zExtraNS = zExtra+1;
}
assert( nClose>=20 );
if( strncmp(zTarget, "http:", 5)==0
|| strncmp(zTarget, "https:", 6)==0
|| strncmp(zTarget, "ftp:", 4)==0
|| strncmp(zTarget, "mailto:", 7)==0
|
| ︙ | ︙ |