1 /*
2  * Dpi for "View source".
3  *
4  * This server is an example. Play with it and modify to your taste.
5  *
6  * Copyright 2010-2015 Jorge Arellano Cid <jcid@dillo.org>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  */
14 
15 #include <unistd.h>
16 #include <sys/types.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <errno.h>
21 #include "../dpip/dpip.h"
22 #include "dpiutil.h"
23 
24 /*
25  * Debugging macros
26  */
27 #define _MSG(...)
28 #define MSG(...)  fprintf(stderr, "[vsource dpi]: " __VA_ARGS__)
29 
30 /*---------------------------------------------------------------------------*/
31 
32 const char *DOCTYPE=
33  "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>";
34 
35 
send_dpip_tag(Dsh * sh,char * dpip_tag)36 void send_dpip_tag(Dsh *sh, char *dpip_tag)
37 {
38    a_Dpip_dsh_write_str(sh, 0, "\nDpip tag received: ");
39    a_Dpip_dsh_write_str(sh, 0, dpip_tag ? dpip_tag : "None");
40    a_Dpip_dsh_write_str(sh, 1, "\n\n");
41 }
42 
43 /*
44  * Send source as plain text
45  * (handles embedded null chars correctly).
46  */
send_plain_text(Dsh * sh,int data_size)47 void send_plain_text(Dsh *sh, int data_size)
48 {
49    char *token;
50    int bytes_read = 0, token_size;
51 
52    /* Send HTTP header for plain text MIME type */
53    a_Dpip_dsh_write_str(sh, 0, "Content-type: text/plain\n\n");
54 
55    while (bytes_read < data_size &&
56           (token = a_Dpip_dsh_read_token2(sh, 1, &token_size))) {
57       bytes_read += token_size;
58       _MSG("data_size=%d bytes_read=%d\n", data_size, bytes_read);
59       a_Dpip_dsh_write(sh, 1, token, token_size);
60       dFree(token);
61    }
62 }
63 
64 /*
65  * Send source as plain text with line numbers
66  * (handles embedded null chars correctly).
67  */
send_numbered_text(Dsh * sh,int data_size)68 void send_numbered_text(Dsh *sh, int data_size)
69 {
70    int bytes_read = 0, line = 1, token_size = 0;
71    char *p, *q, *token, line_str[32];
72 
73    /* Send HTTP header for plain text MIME type */
74    a_Dpip_dsh_write_str(sh, 0, "Content-type: text/plain\n\n");
75 
76    while (bytes_read < data_size &&
77           (token = a_Dpip_dsh_read_token2(sh, 1, &token_size))) {
78       bytes_read += token_size;
79       p = q = token;
80 
81       while (*p) {
82          snprintf(line_str, 32, "%2d: ", line);
83          a_Dpip_dsh_write_str(sh, 0, line_str);
84          if ((p = strpbrk(q, "\r\n"))) {
85             a_Dpip_dsh_write(sh, 0, q, p - q + 1);
86             if (*p == '\r' && p[1] == '\n')
87                ++p;
88             ++line;
89          } else {
90             /* send all the rest */
91             a_Dpip_dsh_write(sh, 1, q, token_size - (q - token));
92             break;
93          }
94          q = ++p;
95       }
96       dFree(token);
97    }
98 }
99 
100 /*
101  * Send source as html text with line numbers
102  * (handles embedded null chars correctly).
103  */
send_html_text(Dsh * sh,const char * url,int data_size)104 void send_html_text(Dsh *sh, const char *url, int data_size)
105 {
106    int bytes_read = 0, old_line = 0, line = 1, token_size = 0;
107    char *p, *q, *token, line_str[128];
108 
109    if (dStrnAsciiCasecmp(url, "dpi:", 4) == 0 &&
110        strncmp(url+4, "/vsource/:", 10) == 0)
111       url += 14;
112 
113    /* Send HTTP header for html text MIME type */
114    a_Dpip_dsh_write_str(sh, 0, "Content-type: text/html\n\n");
115 
116    a_Dpip_dsh_write_str(sh, 0, DOCTYPE);
117    a_Dpip_dsh_printf(sh, 0,
118                      "\n"
119                      "<html><head>\n"
120                      "<title>Source for %s</title>\n"
121                      "<style type=\"text/css\">\n"
122                      " body {white-space: pre-wrap; font-family: monospace}\n"
123                      " td.r1 {background-color:#B87333}\n"
124                      " td.r2 {background-color:#DD7F32}\n"
125                      "</style>\n"
126                      "</head>\n"
127                      "<body id=\"dillo_vs\">\n<table cellpadding='0'>\n", url);
128 
129    while (bytes_read < data_size &&
130           (token = a_Dpip_dsh_read_token2(sh, 1, &token_size))) {
131       bytes_read += token_size;
132       p = q = token;
133 
134       while (*p) {
135          if (line > old_line) {
136             snprintf(line_str, 128,
137                      "<tr><td class='%s'>%d%s<td>",
138                      (line & 1) ? "r1" : "r2", line,
139                      (line == 1 || (line % 10) == 0) ? "&nbsp;" : "");
140             a_Dpip_dsh_write_str(sh, 0, line_str);
141             old_line = line;
142          }
143          if ((p = strpbrk(q, "\r\n<&"))) {
144             if (*p == '\r' || *p == '\n') {
145                a_Dpip_dsh_write(sh, 0, q, p - q + 1);
146                if (*p == '\r' && p[1] == '\n')
147                   p++;
148                ++line;
149             } else {
150                a_Dpip_dsh_write(sh, 0, q, p - q);
151                a_Dpip_dsh_write_str(sh, 0, (*p == '<') ? "&lt;" : "&amp;");
152             }
153          } else {
154             /* send all the rest */
155             a_Dpip_dsh_write(sh, 1, q, token_size - (q - token));
156             break;
157          }
158          q = ++p;
159       }
160       dFree(token);
161    }
162 
163    a_Dpip_dsh_write_str(sh, 1, "</table></body></html>");
164 }
165 
166 /*
167  *
168  */
main(void)169 int main(void)
170 {
171    Dsh *sh;
172    int data_size;
173    char *dpip_tag, *cmd = NULL, *cmd2 = NULL, *url = NULL, *size_str = NULL;
174    char *d_cmd;
175 
176    _MSG("starting...\n");
177    //sleep(20);
178 
179    /* Initialize the SockHandler.
180     * This means we'll use stdin for input and stdout for output.
181     * In case of a server dpi, we'd use a socket and pass its file descriptor
182     * twice (e.g. a_Dpip_dsh_new(sock_fd, sock_fd, 1024).
183     * (Note: by now the last parameter is not used) */
184    sh = a_Dpip_dsh_new(STDIN_FILENO, STDOUT_FILENO, 2*1024);
185 
186    /* Authenticate our client...
187     * As we're using Internet domain sockets, DPIP checks whether the client
188     * runs with the user's ID, by means of a shared secret. The DPIP API does
189     * the work for us. */
190    if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1)) ||
191        a_Dpip_check_auth(dpip_tag) < 0) {
192       MSG("can't authenticate request: %s\n", dStrerror(errno));
193       a_Dpip_dsh_close(sh);
194       return 1;
195    }
196    dFree(dpip_tag);
197 
198    /* Read the dpi command from STDIN
199     * Now we're past the authentication phase, let's see what's dillo
200     * asking from us. a_Dpip_dsh_read_token() will block and return
201     * a full dpip token or null on error (it's commented in dpip.c) */
202    dpip_tag = a_Dpip_dsh_read_token(sh, 1);
203    _MSG("tag = [%s]\n", dpip_tag);
204 
205    /* Now that we have the dpip_tag, let's isolate the command and url */
206    cmd = a_Dpip_get_attr(dpip_tag, "cmd");
207    url = a_Dpip_get_attr(dpip_tag, "url");
208 
209    /* Start sending our answer.
210     * (You can read the comments for DPIP API functions in dpip/dpip.c) */
211    d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
212    a_Dpip_dsh_write_str(sh, 0, d_cmd);
213    dFree(d_cmd);
214    dFree(dpip_tag);
215 
216    dpip_tag = a_Dpip_dsh_read_token(sh, 1);
217    cmd2 = a_Dpip_get_attr(dpip_tag, "cmd");
218    if (cmd2) {
219       if (strcmp(cmd2, "start_send_page") == 0 &&
220           (size_str = a_Dpip_get_attr(dpip_tag, "data_size"))) {
221          data_size = strtol(size_str, NULL, 10);
222          /* Choose your flavour */
223          //send_plain_text(sh, data_size);
224          //send_numbered_text(sh, data_size);
225          send_html_text(sh, url, data_size);
226       } else if (strcmp(cmd2, "DpiError") == 0) {
227          /* Dillo detected an error (other failures just close the socket) */
228          a_Dpip_dsh_write_str(sh, 0, "Content-type: text/plain\n\n");
229          a_Dpip_dsh_write_str(sh, 1, "[vsource dpi]: "
230                                      "ERROR: Page not cached.\n");
231       }
232       dFree(cmd2);
233    }
234 
235    dFree(cmd);
236    dFree(url);
237    dFree(size_str);
238    dFree(dpip_tag);
239 
240    /* Finish the SockHandler */
241    a_Dpip_dsh_close(sh);
242    a_Dpip_dsh_free(sh);
243 
244    return 0;
245 }
246 
247