1 /*
2 * File: capi.c
3 *
4 * Copyright 2002-2007 Jorge Arellano Cid <jcid@dillo.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 */
11
12 /*
13 * Cache API
14 * This is the module that manages the cache and starts the CCC chains
15 * to get the requests served. Kind of a broker.
16 */
17
18 #include <string.h>
19
20 #include "msg.h"
21 #include "capi.h"
22 #include "IO/IO.h" /* for IORead &friends */
23 #include "IO/Url.h"
24 #include "chain.h"
25 #include "history.h"
26 #include "nav.h"
27 #include "dpiapi.h"
28 #include "uicmd.hh"
29 #include "domain.h"
30 #include "../dpip/dpip.h"
31
32 /* for testing dpi chat */
33 #include "bookmark.h"
34
35 typedef struct {
36 DilloUrl *url; /* local copy of web->url */
37 void *bw;
38 char *server;
39 char *datastr;
40 int SockFD;
41 int Flags;
42 ChainLink *InfoSend;
43 ChainLink *InfoRecv;
44
45 int Ref;
46 } capi_conn_t;
47
48 /* Flags for conn */
49 enum {
50 PENDING = 1,
51 TIMEOUT = 2, /* unused */
52 ABORTED = 4
53 };
54
55 /*
56 * Local data
57 */
58 /* Data list for active dpi connections */
59 static Dlist *CapiConns; /* Data list for active connections; it holds
60 * pointers to capi_conn_t structures. */
61 /* Last URL asked for view source */
62 static DilloUrl *CapiVsUrl = NULL;
63
64 /*
65 * Forward declarations
66 */
67 void a_Capi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
68 void *Data1, void *Data2);
69
70
71 /* ------------------------------------------------------------------------- */
72
73 /*
74 * Initialize capi&cache data
75 */
a_Capi_init(void)76 void a_Capi_init(void)
77 {
78 /* create an empty list */
79 CapiConns = dList_new(32);
80 /* init cache */
81 a_Cache_init();
82 }
83
84 /* ------------------------------------------------------------------------- */
85
86 /*
87 * Create a new connection data structure
88 */
89 static capi_conn_t *
Capi_conn_new(const DilloUrl * url,void * bw,char * server,char * datastr)90 Capi_conn_new(const DilloUrl *url, void *bw, char *server, char *datastr)
91 {
92 capi_conn_t *conn;
93
94 conn = dNew(capi_conn_t, 1);
95 conn->url = url ? a_Url_dup(url) : NULL;
96 conn->bw = bw;
97 conn->server = dStrdup(server);
98 conn->datastr = dStrdup(datastr);
99 conn->SockFD = -1;
100 conn->Flags = (strcmp(server, "http") != 0) ? PENDING : 0;
101 conn->InfoSend = NULL;
102 conn->InfoRecv = NULL;
103 conn->Ref = 0; /* Reference count */
104 return conn;
105 }
106
107 /*
108 * Validate a capi_conn_t pointer.
109 * Return value: NULL if not valid, conn otherwise.
110 */
Capi_conn_valid(capi_conn_t * conn)111 static capi_conn_t *Capi_conn_valid(capi_conn_t *conn)
112 {
113 return dList_find(CapiConns, conn);
114 }
115
116 /*
117 * Increment the reference count and add to the list if not present
118 */
Capi_conn_ref(capi_conn_t * conn)119 static void Capi_conn_ref(capi_conn_t *conn)
120 {
121 if (++conn->Ref == 1) {
122 /* add the connection data to list */
123 dList_append(CapiConns, (void *)conn);
124 }
125 _MSG(" Capi_conn_ref #%d %p\n", conn->Ref, conn);
126 }
127
128 /*
129 * Decrement the reference count (and remove from list when zero)
130 */
Capi_conn_unref(capi_conn_t * conn)131 static void Capi_conn_unref(capi_conn_t *conn)
132 {
133 _MSG(" Capi_conn_unref #%d %p\n", conn->Ref - 1, conn);
134
135 /* We may validate conn here, but it doesn't *seem* necessary */
136 if (--conn->Ref == 0) {
137 /* remove conn preserving the list order */
138 dList_remove(CapiConns, (void *)conn);
139 /* free dynamic memory */
140 a_Url_free(conn->url);
141 dFree(conn->server);
142 dFree(conn->datastr);
143 dFree(conn);
144 }
145 _MSG(" Capi_conn_unref CapiConns=%d\n", dList_length(CapiConns));
146 }
147
148 /*
149 * Compare function for searching a conn by server string
150 */
Capi_conn_by_server_cmp(const void * v1,const void * v2)151 static int Capi_conn_by_server_cmp(const void *v1, const void *v2)
152 {
153 const capi_conn_t *node = v1;
154 const char *server = v2;
155 dReturn_val_if_fail(node && node->server && server, 1);
156 return strcmp(node->server, server);
157 }
158
159 /*
160 * Find connection data by server
161 */
Capi_conn_find(char * server)162 static capi_conn_t *Capi_conn_find(char *server)
163 {
164 return dList_find_custom(CapiConns, (void*)server, Capi_conn_by_server_cmp);
165 }
166
167 /*
168 * Resume connections that were waiting for dpid to start.
169 */
Capi_conn_resume(void)170 static void Capi_conn_resume(void)
171 {
172 int i;
173 DataBuf *dbuf;
174 capi_conn_t *conn;
175
176 for (i = 0; i < dList_length(CapiConns); ++i) {
177 conn = dList_nth_data (CapiConns, i);
178 if (conn->Flags & PENDING) {
179 dbuf = a_Chain_dbuf_new(conn->datastr,(int)strlen(conn->datastr), 0);
180 if (conn->InfoSend) {
181 a_Capi_ccc(OpSend, 1, BCK, conn->InfoSend, dbuf, NULL);
182 }
183 dFree(dbuf);
184 conn->Flags &= ~PENDING;
185 }
186 }
187 }
188
189 /*
190 * Abort the connection for a given url, using its CCC.
191 * (OpAbort 2,BCK removes the cache entry)
192 * TODO: when conn is already done, the cache entry isn't removed.
193 * This may be wrong and needs a revision.
194 */
a_Capi_conn_abort_by_url(const DilloUrl * url)195 void a_Capi_conn_abort_by_url(const DilloUrl *url)
196 {
197 int i;
198 capi_conn_t *conn;
199
200 for (i = 0; i < dList_length(CapiConns); ++i) {
201 conn = dList_nth_data (CapiConns, i);
202 if (a_Url_cmp(conn->url, url) == 0) {
203 if (conn->InfoSend) {
204 a_Capi_ccc(OpAbort, 1, BCK, conn->InfoSend, NULL, NULL);
205 }
206 if (conn->InfoRecv) {
207 a_Capi_ccc(OpAbort, 2, BCK, conn->InfoRecv, NULL, NULL);
208 }
209 break;
210 }
211 }
212 }
213
214 /* ------------------------------------------------------------------------- */
215
216 /*
217 * Store the last URL requested by "view source"
218 */
a_Capi_set_vsource_url(const DilloUrl * url)219 void a_Capi_set_vsource_url(const DilloUrl *url)
220 {
221 a_Url_free(CapiVsUrl);
222 CapiVsUrl = a_Url_dup(url);
223 }
224
225 /*
226 * Safety test: only allow GET|POST dpi-urls from dpi-generated pages.
227 */
a_Capi_dpi_verify_request(BrowserWindow * bw,DilloUrl * url)228 int a_Capi_dpi_verify_request(BrowserWindow *bw, DilloUrl *url)
229 {
230 const DilloUrl *referer;
231 int allow = FALSE;
232
233 if (dStrAsciiCasecmp(URL_SCHEME(url), "dpi") == 0) {
234 if (!(URL_FLAGS(url) & (URL_Post + URL_Get))) {
235 allow = TRUE;
236 } else if (!(URL_FLAGS(url) & URL_Post) &&
237 strncmp(URL_PATH(url), "/vsource/", 9) == 0) {
238 allow = TRUE;
239 } else {
240 /* only allow GET&POST dpi-requests from dpi-generated urls */
241 if (a_Nav_stack_size(bw)) {
242 referer = a_History_get_url(NAV_TOP_UIDX(bw));
243 if (dStrAsciiCasecmp(URL_SCHEME(referer), "dpi") == 0) {
244 allow = TRUE;
245 }
246 }
247 }
248 } else {
249 allow = TRUE;
250 }
251
252 if (!allow) {
253 MSG("a_Capi_dpi_verify_request: Permission Denied!\n");
254 MSG(" URL_STR : %s\n", URL_STR(url));
255 if (URL_FLAGS(url) & URL_Post) {
256 MSG(" URL_DATA: %s\n", dStr_printable(URL_DATA(url), 1024));
257 }
258 }
259 return allow;
260 }
261
262 /*
263 * If the url belongs to a dpi server, return its name.
264 */
Capi_url_uses_dpi(DilloUrl * url,char ** server_ptr)265 static int Capi_url_uses_dpi(DilloUrl *url, char **server_ptr)
266 {
267 char *p, *server = NULL, *url_str = URL_STR(url);
268 Dstr *tmp;
269
270 if ((dStrnAsciiCasecmp(url_str, "http:", 5) == 0) ||
271 (dStrnAsciiCasecmp(url_str, "about:", 6) == 0)) {
272 /* URL doesn't use dpi (server = NULL) */
273 } else if (dStrnAsciiCasecmp(url_str, "dpi:/", 5) == 0) {
274 /* dpi prefix, get this server's name */
275 if ((p = strchr(url_str + 5, '/')) != NULL) {
276 server = dStrndup(url_str + 5, (uint_t)(p - url_str - 5));
277 } else {
278 server = dStrdup("?");
279 }
280 if (strcmp(server, "bm") == 0) {
281 dFree(server);
282 server = dStrdup("bookmarks");
283 }
284 } else if ((p = strchr(url_str, ':')) != NULL) {
285 tmp = dStr_new("proto.");
286 dStr_append_l(tmp, url_str, p - url_str);
287 server = tmp->str;
288 dStr_free(tmp, 0);
289 }
290
291 return ((*server_ptr = server) ? 1 : 0);
292 }
293
294 /*
295 * Build the dpip command tag, according to URL and server.
296 */
Capi_dpi_build_cmd(DilloWeb * web,char * server)297 static char *Capi_dpi_build_cmd(DilloWeb *web, char *server)
298 {
299 char *cmd;
300
301 if (strcmp(server, "proto.https") == 0) {
302 /* Let's be kind and make the HTTP query string for the dpi */
303 char *proxy_connect = a_Http_make_connect_str(web->url);
304 Dstr *http_query = a_Http_make_query_str(web->url, web->requester,
305 web->flags, FALSE);
306
307 if ((uint_t) http_query->len > strlen(http_query->str)) {
308 /* Can't handle NULLs embedded in query data */
309 MSG_ERR("HTTPS query truncated!\n");
310 }
311
312 /* BUG: WORKAROUND: request to only check the root URL's certificate.
313 * This avoids the dialog bombing that stems from loading multiple
314 * https images/resources in a single page. A proper fix would take
315 * either to implement the https-dpi as a server (with state),
316 * or to move back https handling into dillo. */
317 if (proxy_connect) {
318 const char *proxy_urlstr = a_Http_get_proxy_urlstr();
319 cmd = a_Dpip_build_cmd("cmd=%s proxy_url=%s proxy_connect=%s "
320 "url=%s query=%s check_cert=%s",
321 "open_url", proxy_urlstr,
322 proxy_connect, URL_STR(web->url),
323 http_query->str,
324 (web->flags & WEB_RootUrl) ? "true" : "false");
325 } else {
326 cmd = a_Dpip_build_cmd("cmd=%s url=%s query=%s check_cert=%s",
327 "open_url", URL_STR(web->url),http_query->str,
328 (web->flags & WEB_RootUrl) ? "true" : "false");
329 }
330 dFree(proxy_connect);
331 dStr_free(http_query, 1);
332
333 } else if (strcmp(server, "downloads") == 0) {
334 /* let the downloads server get it */
335 cmd = a_Dpip_build_cmd("cmd=%s url=%s destination=%s",
336 "download", URL_STR(web->url), web->filename);
337
338 } else {
339 /* For everyone else, the url string is enough... */
340 cmd = a_Dpip_build_cmd("cmd=%s url=%s", "open_url", URL_STR(web->url));
341 }
342 return cmd;
343 }
344
345 /*
346 * Send the requested URL's source to the "view source" dpi
347 */
Capi_dpi_send_source(BrowserWindow * bw,DilloUrl * url)348 static void Capi_dpi_send_source(BrowserWindow *bw, DilloUrl *url)
349 {
350 char *p, *buf, *cmd, size_str[32], *server="vsource";
351 int buf_size;
352
353 if (!(p = strchr(URL_STR(url), ':')) || !(p = strchr(p + 1, ':')))
354 return;
355
356 if (a_Capi_get_buf(CapiVsUrl, &buf, &buf_size)) {
357 /* send the page's source to this dpi connection */
358 snprintf(size_str, 32, "%d", buf_size);
359 cmd = a_Dpip_build_cmd("cmd=%s url=%s data_size=%s",
360 "start_send_page", URL_STR(url), size_str);
361 a_Capi_dpi_send_cmd(NULL, bw, cmd, server, 0);
362 a_Capi_dpi_send_data(url, bw, buf, buf_size, server, 0);
363 } else {
364 cmd = a_Dpip_build_cmd("cmd=%s msg=%s",
365 "DpiError", "Page is NOT cached");
366 a_Capi_dpi_send_cmd(NULL, bw, cmd, server, 0);
367 }
368 dFree(cmd);
369 }
370
371 /*
372 * Most used function for requesting a URL.
373 * TODO: clean up the ad-hoc bindings with an API that allows dynamic
374 * addition of new plugins.
375 *
376 * Return value: A primary key for identifying the client,
377 * 0 if the client is aborted in the process.
378 */
a_Capi_open_url(DilloWeb * web,CA_Callback_t Call,void * CbData)379 int a_Capi_open_url(DilloWeb *web, CA_Callback_t Call, void *CbData)
380 {
381 int reload;
382 char *cmd, *server;
383 capi_conn_t *conn = NULL;
384 const char *scheme = URL_SCHEME(web->url);
385 int safe = 0, ret = 0, use_cache = 0;
386
387 /* web->requester is NULL if the action is initiated by user */
388 if (!(a_Capi_get_flags(web->url) & CAPI_IsCached ||
389 web->requester == NULL ||
390 a_Domain_permit(web->requester, web->url))) {
391 return 0;
392 }
393
394 /* reload test */
395 reload = (!(a_Capi_get_flags(web->url) & CAPI_IsCached) ||
396 (URL_FLAGS(web->url) & URL_E2EQuery));
397
398 if (web->flags & WEB_Download) {
399 /* download request: if cached save from cache, else
400 * for http, ftp or https, use the downloads dpi */
401 if (a_Capi_get_flags_with_redirection(web->url) & CAPI_IsCached) {
402 if (web->filename) {
403 if ((web->stream = fopen(web->filename, "w"))) {
404 use_cache = 1;
405 } else {
406 MSG_WARN("Cannot open \"%s\" for writing.\n", web->filename);
407 }
408 }
409 } else if (a_Cache_download_enabled(web->url)) {
410 server = "downloads";
411 cmd = Capi_dpi_build_cmd(web, server);
412 a_Capi_dpi_send_cmd(web->url, web->bw, cmd, server, 1);
413 dFree(cmd);
414 } else {
415 MSG_WARN("Ignoring download request for '%s': "
416 "not in cache and not downloadable.\n",
417 URL_STR(web->url));
418 }
419
420 } else if (Capi_url_uses_dpi(web->url, &server)) {
421 /* dpi request */
422 if ((safe = a_Capi_dpi_verify_request(web->bw, web->url))) {
423 if (dStrAsciiCasecmp(scheme, "dpi") == 0) {
424 if (strcmp(server, "vsource") == 0) {
425 /* allow "view source" reload upon user request */
426 } else {
427 /* make the other "dpi:/" prefixed urls always reload. */
428 a_Url_set_flags(web->url, URL_FLAGS(web->url) | URL_E2EQuery);
429 reload = 1;
430 }
431 }
432 if (reload) {
433 a_Capi_conn_abort_by_url(web->url);
434 /* Send dpip command */
435 _MSG("a_Capi_open_url, reload url='%s'\n", URL_STR(web->url));
436 cmd = Capi_dpi_build_cmd(web, server);
437 a_Capi_dpi_send_cmd(web->url, web->bw, cmd, server, 1);
438 dFree(cmd);
439 if (strcmp(server, "vsource") == 0) {
440 Capi_dpi_send_source(web->bw, web->url);
441 }
442 }
443 use_cache = 1;
444 }
445 dFree(server);
446
447 } else if (!dStrAsciiCasecmp(scheme, "http")) {
448 /* http request */
449 if (reload) {
450 a_Capi_conn_abort_by_url(web->url);
451 /* create a new connection and start the CCC operations */
452 conn = Capi_conn_new(web->url, web->bw, "http", "none");
453 /* start the reception branch before the query one because the DNS
454 * may callback immediately. This may avoid a race condition. */
455 a_Capi_ccc(OpStart, 2, BCK, a_Chain_new(), conn, "http");
456 a_Capi_ccc(OpStart, 1, BCK, a_Chain_new(), conn, web);
457 }
458 use_cache = 1;
459
460 } else if (!dStrAsciiCasecmp(scheme, "about")) {
461 /* internal request */
462 use_cache = 1;
463 }
464
465 if (use_cache) {
466 if (!conn || (conn && Capi_conn_valid(conn))) {
467 /* not aborted, let's continue... */
468 ret = a_Cache_open_url(web, Call, CbData);
469 }
470 } else {
471 a_Web_free(web);
472 }
473 return ret;
474 }
475
476 /*
477 * Convert cache-defined flags to Capi ones.
478 */
Capi_map_cache_flags(uint_t flags)479 static int Capi_map_cache_flags(uint_t flags)
480 {
481 int status = 0;
482
483 if (flags) {
484 status |= CAPI_IsCached;
485 if (flags & CA_IsEmpty)
486 status |= CAPI_IsEmpty;
487 if (flags & CA_GotData)
488 status |= CAPI_Completed;
489 else
490 status |= CAPI_InProgress;
491
492 /* CAPI_Aborted is not yet used/defined */
493 }
494 return status;
495 }
496
497 /*
498 * Return status information of an URL's content-transfer process.
499 */
a_Capi_get_flags(const DilloUrl * Url)500 int a_Capi_get_flags(const DilloUrl *Url)
501 {
502 uint_t flags = a_Cache_get_flags(Url);
503 int status = flags ? Capi_map_cache_flags(flags) : 0;
504 return status;
505 }
506
507 /*
508 * Same as a_Capi_get_flags() but following redirections.
509 */
a_Capi_get_flags_with_redirection(const DilloUrl * Url)510 int a_Capi_get_flags_with_redirection(const DilloUrl *Url)
511 {
512 uint_t flags = a_Cache_get_flags_with_redirection(Url);
513 int status = flags ? Capi_map_cache_flags(flags) : 0;
514 return status;
515 }
516
517 /*
518 * Get the cache's buffer for the URL, and its size.
519 * Return: 1 cached, 0 not cached.
520 */
a_Capi_get_buf(const DilloUrl * Url,char ** PBuf,int * BufSize)521 int a_Capi_get_buf(const DilloUrl *Url, char **PBuf, int *BufSize)
522 {
523 return a_Cache_get_buf(Url, PBuf, BufSize);
524 }
525
526 /*
527 * Unref the cache's buffer when no longer using it.
528 */
a_Capi_unref_buf(const DilloUrl * Url)529 void a_Capi_unref_buf(const DilloUrl *Url)
530 {
531 a_Cache_unref_buf(Url);
532 }
533
534 /*
535 * Get the Content-Type associated with the URL
536 */
a_Capi_get_content_type(const DilloUrl * url)537 const char *a_Capi_get_content_type(const DilloUrl *url)
538 {
539 return a_Cache_get_content_type(url);
540 }
541
542 /*
543 * Set the Content-Type for the URL.
544 */
a_Capi_set_content_type(const DilloUrl * url,const char * ctype,const char * from)545 const char *a_Capi_set_content_type(const DilloUrl *url, const char *ctype,
546 const char *from)
547 {
548 return a_Cache_set_content_type(url, ctype, from);
549 }
550
551 /*
552 * Send data to a dpi (e.g. add_bookmark, open_url, send_preferences, ...)
553 * Most of the time we send dpi commands, but it also serves for raw data
554 * as with "view source".
555 */
a_Capi_dpi_send_data(const DilloUrl * url,void * bw,char * data,int data_sz,char * server,int flags)556 int a_Capi_dpi_send_data(const DilloUrl *url, void *bw,
557 char *data, int data_sz, char *server, int flags)
558 {
559 capi_conn_t *conn;
560 DataBuf *dbuf;
561
562 if (flags & 1) {
563 /* open a new connection to server */
564
565 /* Create a new connection data struct and add it to the list */
566 conn = Capi_conn_new(url, bw, server, data);
567 /* start the CCC operations */
568 a_Capi_ccc(OpStart, 2, BCK, a_Chain_new(), conn, server);
569 a_Capi_ccc(OpStart, 1, BCK, a_Chain_new(), conn, server);
570
571 } else {
572 /* Re-use an open connection */
573 conn = Capi_conn_find(server);
574 if (conn && conn->InfoSend) {
575 /* found & active */
576 dbuf = a_Chain_dbuf_new(data, data_sz, 0);
577 a_Capi_ccc(OpSend, 1, BCK, conn->InfoSend, dbuf, NULL);
578 dFree(dbuf);
579 } else {
580 MSG(" ERROR: [a_Capi_dpi_send_data] No open connection found\n");
581 }
582 }
583
584 return 0;
585 }
586
587 /*
588 * Send a dpi cmd.
589 * (For instance: add_bookmark, open_url, send_preferences, ...)
590 */
a_Capi_dpi_send_cmd(DilloUrl * url,void * bw,char * cmd,char * server,int flags)591 int a_Capi_dpi_send_cmd(DilloUrl *url, void *bw, char *cmd, char *server,
592 int flags)
593 {
594 return a_Capi_dpi_send_data(url, bw, cmd, strlen(cmd), server, flags);
595 }
596
597 /*
598 * Remove a client from the cache client queue.
599 * force = also abort the CCC if this is the last client.
600 */
a_Capi_stop_client(int Key,int force)601 void a_Capi_stop_client(int Key, int force)
602 {
603 CacheClient_t *Client;
604
605 _MSG("a_Capi_stop_client: force=%d\n", force);
606
607 Client = a_Cache_client_get_if_unique(Key);
608 if (Client && (force || Client->BufSize == 0)) {
609 /* remove empty entries too */
610 a_Capi_conn_abort_by_url(Client->Url);
611 }
612 a_Cache_stop_client(Key);
613 }
614
615 /*
616 * CCC function for the CAPI module
617 */
a_Capi_ccc(int Op,int Branch,int Dir,ChainLink * Info,void * Data1,void * Data2)618 void a_Capi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
619 void *Data1, void *Data2)
620 {
621 capi_conn_t *conn;
622
623 dReturn_if_fail( a_Chain_check("a_Capi_ccc", Op, Branch, Dir, Info) );
624
625 if (Branch == 1) {
626 if (Dir == BCK) {
627 /* Command sending branch */
628 switch (Op) {
629 case OpStart:
630 /* Data1 = conn; Data2 = {Web | server} */
631 conn = Data1;
632 Capi_conn_ref(conn);
633 Info->LocalKey = conn;
634 conn->InfoSend = Info;
635 if (strcmp(conn->server, "http") == 0) {
636 a_Chain_link_new(Info, a_Capi_ccc, BCK, a_Http_ccc, 1, 1);
637 a_Chain_bcb(OpStart, Info, Data2, NULL);
638 } else {
639 a_Chain_link_new(Info, a_Capi_ccc, BCK, a_Dpi_ccc, 1, 1);
640 a_Chain_bcb(OpStart, Info, Data2, NULL);
641 }
642 break;
643 case OpSend:
644 /* Data1 = dbuf */
645 a_Chain_bcb(OpSend, Info, Data1, NULL);
646 break;
647 case OpEnd:
648 conn = Info->LocalKey;
649 conn->InfoSend = NULL;
650 a_Chain_bcb(OpEnd, Info, NULL, NULL);
651 Capi_conn_unref(conn);
652 dFree(Info);
653 break;
654 case OpAbort:
655 conn = Info->LocalKey;
656 conn->InfoSend = NULL;
657 a_Chain_bcb(OpAbort, Info, NULL, NULL);
658 Capi_conn_unref(conn);
659 dFree(Info);
660 break;
661 default:
662 MSG_WARN("Unused CCC\n");
663 break;
664 }
665 } else { /* 1 FWD */
666 /* Command sending branch (status) */
667 switch (Op) {
668 case OpSend:
669 if (!Data2) {
670 MSG_WARN("Capi.c: Opsend [1F] Data2 = NULL\n");
671 } else if (strcmp(Data2, "FD") == 0) {
672 conn = Info->LocalKey;
673 conn->SockFD = *(int*)Data1;
674 /* communicate the FD through the answer branch */
675 a_Capi_ccc(OpSend, 2, BCK, conn->InfoRecv, &conn->SockFD, "FD");
676 } else if (strcmp(Data2, "DpidOK") == 0) {
677 /* resume pending dpi requests */
678 Capi_conn_resume();
679 }
680 break;
681 case OpAbort:
682 conn = Info->LocalKey;
683 conn->InfoSend = NULL;
684 if (Data2) {
685 if (!strcmp(Data2, "DpidERROR")) {
686 a_UIcmd_set_msg(conn->bw,
687 "ERROR: can't start dpid daemon "
688 "(URL scheme = '%s')!",
689 conn->url ? URL_SCHEME(conn->url) : "");
690 } else if (!strcmp(Data2, "Both") && conn->InfoRecv) {
691 /* abort the other branch too */
692 a_Capi_ccc(OpAbort, 2, BCK, conn->InfoRecv, NULL, NULL);
693 }
694 }
695 /* if URL == expect-url */
696 a_Nav_cancel_expect_if_eq(conn->bw, conn->url);
697 /* finish conn */
698 Capi_conn_unref(conn);
699 dFree(Info);
700 break;
701 default:
702 MSG_WARN("Unused CCC\n");
703 break;
704 }
705 }
706
707 } else if (Branch == 2) {
708 if (Dir == BCK) {
709 /* Answer branch */
710 switch (Op) {
711 case OpStart:
712 /* Data1 = conn; Data2 = {"http" | "<dpi server name>"} */
713 conn = Data1;
714 Capi_conn_ref(conn);
715 Info->LocalKey = conn;
716 conn->InfoRecv = Info;
717 a_Chain_link_new(Info, a_Capi_ccc, BCK, a_Dpi_ccc, 2, 2);
718 a_Chain_bcb(OpStart, Info, NULL, Data2);
719 break;
720 case OpSend:
721 /* Data1 = FD */
722 if (Data2 && strcmp(Data2, "FD") == 0) {
723 a_Chain_bcb(OpSend, Info, Data1, Data2);
724 }
725 break;
726 case OpAbort:
727 conn = Info->LocalKey;
728 conn->InfoRecv = NULL;
729 a_Chain_bcb(OpAbort, Info, NULL, NULL);
730 /* remove the cache entry for this URL */
731 a_Cache_entry_remove_by_url(conn->url);
732 Capi_conn_unref(conn);
733 dFree(Info);
734 break;
735 default:
736 MSG_WARN("Unused CCC\n");
737 break;
738 }
739 } else { /* 2 FWD */
740 /* Server listening branch */
741 switch (Op) {
742 case OpSend:
743 conn = Info->LocalKey;
744 if (strcmp(Data2, "send_page_2eof") == 0) {
745 /* Data1 = dbuf */
746 DataBuf *dbuf = Data1;
747 a_Cache_process_dbuf(IORead, dbuf->Buf, dbuf->Size, conn->url);
748 } else if (strcmp(Data2, "send_status_message") == 0) {
749 a_UIcmd_set_msg(conn->bw, "%s", Data1);
750 } else if (strcmp(Data2, "chat") == 0) {
751 a_UIcmd_set_msg(conn->bw, "%s", Data1);
752 a_Bookmarks_chat_add(NULL, NULL, Data1);
753 } else if (strcmp(Data2, "dialog") == 0) {
754 a_Dpiapi_dialog(conn->bw, conn->server, Data1);
755 } else if (strcmp(Data2, "reload_request") == 0) {
756 a_Nav_reload(conn->bw);
757 } else if (strcmp(Data2, "start_send_page") == 0) {
758 /* prepare the cache to receive the data stream for this URL
759 *
760 * a_Capi_open_url() already added a new cache entry,
761 * and a client for it.
762 */
763 }
764 break;
765 case OpEnd:
766 conn = Info->LocalKey;
767 conn->InfoRecv = NULL;
768
769 a_Cache_process_dbuf(IOClose, NULL, 0, conn->url);
770
771 if (conn->InfoSend) {
772 /* Propagate OpEnd to the sending branch too */
773 a_Capi_ccc(OpEnd, 1, BCK, conn->InfoSend, NULL, NULL);
774 }
775 Capi_conn_unref(conn);
776 dFree(Info);
777 break;
778 default:
779 MSG_WARN("Unused CCC\n");
780 break;
781 }
782 }
783 }
784 }
785