OSDN Git Service

fix User-Agent header version number.
[bbk/bchan.git] / src / submitutil.c
1 /*
2  * submitutil.c
3  *
4  * Copyright (c) 2010-2011 project bchan
5  *
6  * This software is provided 'as-is', without any express or implied
7  * warranty. In no event will the authors be held liable for any damages
8  * arising from the use of this software.
9  *
10  * Permission is granted to anyone to use this software for any purpose,
11  * including commercial applications, and to alter it and redistribute it
12  * freely, subject to the following restrictions:
13  *
14  * 1. The origin of this software must not be misrepresented; you must not
15  *    claim that you wrote the original software. If you use this software
16  *    in a product, an acknowledgment in the product documentation would be
17  *    appreciated but is not required.
18  *
19  * 2. Altered source versions must be plainly marked as such, and must not be
20  *    misrepresented as being the original software.
21  *
22  * 3. This notice may not be removed or altered from any source
23  *    distribution.
24  *
25  */
26
27 #include        <basic.h>
28 #include        <bstdio.h>
29 #include        <bstdlib.h>
30 #include        <bstring.h>
31
32 #include    "submitutil.h"
33 #include    "sjisstring.h"
34 #include    "parselib.h"
35 #include    "cookiedb.h"
36 #include    "setcookieheader.h"
37
38 #define MAKEHEADER_ERR_LEN(dest, dest_len, src, len) \
39    err = sjstring_appendasciistring(dest, dest_len, src, len); \
40    if(err < 0){ \
41      return err; \
42    }
43
44 #define MAKEHEADER_ERR(dest, dest_len, src) MAKEHEADER_ERR_LEN(dest, dest_len, (src), strlen((src)))
45
46 #define MAKEHEADER_NUM_ERR(dest, dest_len, num) \
47    err = sjstring_appendUWstring(dest, dest_len, num); \
48    if(err < 0){ \
49      return err; \
50    }
51
52 EXPORT submitutil_poststatus_t submitutil_checkresponse(UB *body, W len)
53 {
54         W cmp;
55         UB *ptr, *title_last, *found;
56         UB title_true[] = {0x82, 0xdc, 0x82, 0xb5, 0x82, 0xbd, '\0'};
57         UB title_error[] = {0x82, 0x64, 0x82, 0x71, 0x82, 0x71, '\0'};
58         UB title_cookie[] = {0x8a, 0x6d, 0x94, 0x46, '\0'};
59         UB title_ocha[] = {0x82, 0xa8, 0x92, 0x83, '\0'};
60
61         ptr = body;
62         for (;;) {
63                 ptr = strstr(ptr, "<!--");
64                 if (ptr == NULL) {
65                         break;
66                 }
67                 ptr++;
68                 for (;ptr < body + len;ptr++) {
69                         if (*ptr != ' ') {
70                                 break;
71                         }
72                 }
73                 cmp = strcmp(ptr, "2ch_X:true");
74                 if (cmp == 0) {
75                         return submitutil_poststatus_true;
76                 }
77                 cmp = strcmp(ptr, "2ch_X:false");
78                 if (cmp == 0) {
79                         return submitutil_poststatus_false;
80                 }
81                 cmp = strcmp(ptr, "2ch_X:error");
82                 if (cmp == 0) {
83                         return submitutil_poststatus_error;
84                 }
85                 cmp = strcmp(ptr, "2ch_X:check");
86                 if (cmp == 0) {
87                         return submitutil_poststatus_check;
88                 }
89                 cmp = strcmp(ptr, "2ch_X:cookie");
90                 if (cmp == 0) {
91                         return submitutil_poststatus_cookie;
92                 }
93         }
94
95         for (ptr = body;ptr < body + len; ptr++) {
96                 if (*ptr == '<') {
97                         cmp = strncasecmp(ptr, "<title>", 7);
98                         if (cmp != 0) {
99                                 continue;
100                         }
101                         ptr += 7;
102                         title_last = strstr(ptr, "</");
103                         if (title_last == NULL) {
104                                 return submitutil_poststatus_notfound;
105                         }
106                         found = strstr(ptr, title_true);
107                         if ((found != NULL)&&(found < title_last)) {
108                                 return submitutil_poststatus_true;
109                         }
110                         found = strstr(ptr, title_error);
111                         if ((found != NULL)&&(found < title_last)) {
112                                 return submitutil_poststatus_error;
113                         }
114                         found = strstr(ptr, title_cookie);
115                         if ((found != NULL)&&(found < title_last)) {
116                                 return submitutil_poststatus_cookie;
117                         }
118                         found = strstr(ptr, title_ocha);
119                         if ((found != NULL)&&(found < title_last)) {
120                                 return submitutil_poststatus_error;
121                         }
122                 }
123         }
124
125         return submitutil_poststatus_notfound;
126 }
127
128 #ifdef BCHAN_CONFIG_DEBUG
129 EXPORT VOID SUBMITUTIL_POSTSTATUS_DP(submitutil_poststatus_t status)
130 {
131         switch (status) {
132         case submitutil_poststatus_notfound:
133                 printf("submitutil_poststatus_notfound\n");
134                 break;
135         case submitutil_poststatus_true:
136                 printf("submitutil_poststatus_true\n");
137                 break;
138         case submitutil_poststatus_false:
139                 printf("submitutil_poststatus_false\n");
140                 break;
141         case submitutil_poststatus_error:
142                 printf("submitutil_poststatus_error\n");
143                 break;
144         case submitutil_poststatus_check:
145                 printf("submitutil_poststatus_check\n");
146                 break;
147         case submitutil_poststatus_cookie:
148                 printf("submitutil_poststatus_cookie\n");
149                 break;
150         }
151 }
152 #endif
153
154 LOCAL W submitutil_appendstring_chref_before_urlencode(UB **dest, W *dlen, UB *src, W slen)
155 {
156         charreferparser_t parser;
157         charreferparser_result_t result;
158         UB *amp, *rem, ch_b;
159         W i, rem_len, ch_w, err;
160
161         rem = src;
162         rem_len = slen;
163
164         for (;;) {
165                 amp = sjstring_searchchar(rem, rem_len, '&');
166                 if (amp == NULL) {
167                         return sjstring_appendurlencodestring(dest, dlen, rem, rem_len);
168                 }
169                 err = sjstring_appendurlencodestring(dest, dlen, rem, amp - rem);
170                 if (err < 0) {
171                         return err;
172                 }
173
174                 rem_len -= amp - rem;
175
176                 err = charreferparser_initialize(&parser);
177                 if (err < 0) {
178                         return err;
179                 }
180                 for (i = 0; i < rem_len; i++) {
181                         result = charreferparser_parsechar(&parser, amp[i]);
182                         if (result == CHARREFERPARSER_RESULT_DETERMINE) {
183                                 ch_w = charreferparser_getcharnumber(&parser);
184                                 if ((ch_w >= 0)&&(ch_w < 256)) {
185                                         ch_b = ch_w & 0xff;
186                                         if (ch_b == 0x0a) { /* need more check ? */
187                                                 UB ch_b2 = 0x0d;
188                                                 err = sjstring_appendurlencodestring(dest, dlen, &ch_b2, 1);
189                                                 if (err < 0) {
190                                                         return err;
191                                                 }
192                                         }
193                                         err = sjstring_appendurlencodestring(dest, dlen, &ch_b, 1);
194                                         if (err < 0) {
195                                                 return err;
196                                         }
197                                 } else {
198                                         err = sjstring_appendurlencodestring(dest, dlen, amp, i+1);
199                                         if (err < 0) {
200                                                 return err;
201                                         }
202                                 }
203                                 i++;
204                                 break;
205                         }
206                         if (result == CHARREFERPARSER_RESULT_INVALID) {
207                                 break;
208                         }
209                 }
210                 charreferparser_finalize(&parser);
211
212                 rem_len -= i;
213                 rem = amp + i;
214
215                 if (rem_len <= 0) {
216                         break;
217                 }
218         }
219
220         return 0;
221 }
222
223 EXPORT W submitutil_makenextrequestbody(UB *prev_body, W prev_body_len, UB **next_body, W *next_len)
224 {
225         UB *ptr;
226         UB *newbody = NULL;
227         UB *name = NULL, *value = NULL;
228         W name_len = 0, value_len = 0;
229         W newbody_len = 0;
230         W err;
231         Bool first = True;
232
233         for (ptr = prev_body; ptr < prev_body + prev_body_len;) {
234                 ptr = strstr(ptr, "<input ");
235                 if (ptr == NULL) {
236                         break;
237                 }
238                 name = NULL;
239                 value = NULL;
240                 name_len = 0;
241                 value_len = 0;
242                 for (;ptr < prev_body + prev_body_len;) {
243                         for (;ptr < prev_body + prev_body_len;ptr++) {
244                                 if (*ptr != ' ') {
245                                         break;
246                                 }
247                         }
248                         if (*ptr == '>') {
249                                 if ((name == NULL)||(name_len == 0)) {
250                                         break;
251                                 }
252                                 if (first != True) {
253                                         err = sjstring_appendasciistring(&newbody, &newbody_len, "&", 1);
254                                         if (err < 0) {
255                                                 return err;
256                                         }
257                                 }
258                                 first = False;
259                                 err = sjstring_appendasciistring(&newbody, &newbody_len, name, name_len);
260                                 if (err < 0) {
261                                         return err;
262                                 }
263                                 sjstring_appendasciistring(&newbody, &newbody_len, "=", 1);
264                                 if (err < 0) {
265                                         return err;
266                                 }
267                                 err = submitutil_appendstring_chref_before_urlencode(&newbody, &newbody_len, value, value_len);
268                                 if (err < 0) {
269                                         return err;
270                                 }
271                                 break;
272                         }
273                         /* check attribute */
274                         if (strncasecmp(ptr, "name=\"", 6) == 0) {
275                                 ptr += 6;
276                                 name = ptr;
277                                 for (;ptr < prev_body + prev_body_len;ptr++) {
278                                         if (*ptr == '"') {
279                                                 break;
280                                         }
281                                 }
282                                 name_len = ptr - name;
283                                 ptr++;
284                         } else if (strncasecmp(ptr, "name=", 5) == 0) {
285                                 ptr += 5;
286                                 name = ptr;
287                                 for (;ptr < prev_body + prev_body_len;ptr++) {
288                                         if ((*ptr == ' ')||(*ptr == '>')) {
289                                                 break;
290                                         }
291                                 }
292                                 name_len = ptr - name;
293                         } else if (strncasecmp(ptr, "value=\"", 7) == 0) {
294                                 ptr += 7;
295                                 value = ptr;
296                                 for (;ptr < prev_body + prev_body_len;ptr++) {
297                                         if (*ptr == '"') {
298                                                 break;
299                                         }
300                                 }
301                                 value_len = ptr - value;
302                                 ptr++;
303                         } else if (strncasecmp(ptr, "value=", 6) == 0) {
304                                 ptr += 6;
305                                 value = ptr;
306                                 for (;ptr < prev_body + prev_body_len;ptr++) {
307                                         if ((*ptr == ' ')||(*ptr == '>')) {
308                                                 break;
309                                         }
310                                 }
311                                 value_len = ptr - value;
312                         } else {
313                                 /* skip attribute */
314                                 for (;ptr < prev_body + prev_body_len;ptr++) {
315                                         if (*ptr == ' ') {
316                                                 break;
317                                         }
318                                 }
319                         }
320                 }
321         }
322
323         *next_body = newbody;
324         *next_len = newbody_len;
325
326         return 0;
327 }
328
329 LOCAL W submitutil_setcookie_inputparserresult(cookiedb_readheadercontext_t *ctx, setcookieparser_result_t *result)
330 {
331         W err, i;
332         W (*proc)(cookiedb_readheadercontext_t *context, UB ch);
333
334         err = 0;
335         switch (result->type) {
336         case SETCOOKIEPARSER_RESULT_TYPE_NAMEATTR:
337                 for (i = 0; i < result->val.name.len; i++) {
338                         err = cookiedb_readheadercontext_appendchar_attr(ctx, result->val.name.str[i]);
339                         if (err < 0) {
340                                 break;
341                         }
342                 }
343                 break;
344         case SETCOOKIEPARSER_RESULT_TYPE_VALUE:
345                 switch (result->val.value.attr) {
346                 case SETCOOKIEPARSER_ATTR_COMMENT:
347                         proc = cookiedb_readheadercontext_appendchar_comment;
348                         break;
349                 case SETCOOKIEPARSER_ATTR_DOMAIN:
350                         proc = cookiedb_readheadercontext_appendchar_domain;
351                         break;
352                 case SETCOOKIEPARSER_ATTR_PATH:
353                         proc = cookiedb_readheadercontext_appendchar_path;
354                         break;
355                 case SETCOOKIEPARSER_ATTR_VERSION:
356                         proc = cookiedb_readheadercontext_appendchar_version;
357                         break;
358                 case SETCOOKIEPARSER_ATTR_NAME:
359                         proc = cookiedb_readheadercontext_appendchar_name;
360                         break;
361                 case SETCOOKIEPARSER_ATTR_EXPIRES:
362                 case SETCOOKIEPARSER_ATTR_MAX_AGE:
363                 case SETCOOKIEPARSER_ATTR_SECURE:
364                 default:
365                         proc = NULL;
366                         break;
367                 }
368                 if (proc != NULL) {
369                         for (i = 0; i < result->val.value.len; i++) {
370                                 err = (*proc)(ctx, result->val.value.str[i]);
371                                 if (err < 0) {
372                                         break;
373                                 }
374                         }
375                 }
376                 break;
377         case SETCOOKIEPARSER_RESULT_TYPE_SECUREATTR:
378                 err = cookiedb_readheadercontext_setsecure(ctx);
379                 break;
380         case SETCOOKIEPARSER_RESULT_TYPE_EXPIRESATTR:
381                 err = cookiedb_readheadercontext_setexpires(ctx, result->val.expires.time);
382                 break;
383         }
384
385         return err;
386 }
387
388 LOCAL W submitutil_setcookie(setcookieparser_t *parser, cookiedb_t *db, UB *str, W len, UB *host, W host_len, UB *path, W path_len, STIME time)
389 {
390         cookiedb_readheadercontext_t *ctx;
391         setcookieparser_result_t *result;
392         W i, j, err, ret, result_len;
393
394         ctx = cookiedb_startheaderread(db, host, host_len, path, path_len, time);
395         if (ctx == NULL) {
396                 return -1; /* TODO */
397         }
398
399         err = 0;
400         for (i = 0; i < len; i++) {
401                 if (str[i] == '\r') {
402                         break;
403                 }
404                 ret = setcookieparser_inputchar(parser, str[i], &result, &result_len);
405                 if (ret != SETCOOKIEPARSER_CONTINUE) {
406                         break;
407                 }
408                 err = 0;
409                 for (j = 0; j < result_len; j++) {
410                         err = submitutil_setcookie_inputparserresult(ctx, result + j);
411                         if (err < 0) {
412                                 break;
413                         }
414                 }
415                 if (err < 0) {
416                         break;
417                 }
418         }
419         if (err == 0) {
420                 ret = setcookieparser_endinput(parser, &result, &result_len);
421                 if (ret == SETCOOKIEPARSER_CONTINUE) {
422                         for (j = 0; j < result_len; j++) {
423                                 err = submitutil_setcookie_inputparserresult(ctx, result + j);
424                         }
425                 }
426         }
427
428         cookiedb_endheaderread(db, ctx);
429
430         return err;
431 }
432
433 EXPORT W submitutil_updatecookiedb(cookiedb_t *db, UB *prev_header, W prev_header_len, UB *host, W host_len, STIME time)
434 {
435         UB *ptr, *start;
436         W err;
437         setcookieparser_t parser;
438         UB *path = "/test/bbs.cgi";
439         W path_len = strlen(path);
440
441         for (ptr = prev_header; ptr < prev_header + prev_header_len;) {
442                 ptr = strstr(ptr, "Set-Cookie:");
443                 if (ptr == NULL) {
444                         break;
445                 }
446                 ptr += 11;
447                 for (;ptr < prev_header + prev_header_len; ptr++) {
448                         if (*ptr != ' ') {
449                                 break;
450                         }
451                 }
452                 start = ptr;
453                 for (;ptr < prev_header + prev_header_len; ptr++) {
454                         if (*ptr == '\r') {
455                                 ptr++;
456                                 if (*ptr == '\n') {
457                                         ptr++;
458                                         break;
459                                 }
460                         }
461                 }
462                 err = setcookieparser_initialize(&parser);
463                 if (err < 0) {
464                         return err;
465                 }
466                 err = submitutil_setcookie(&parser, db, start, ptr - start, host, host_len, path, path_len, time);
467                 setcookieparser_finalize(&parser);
468                 if (err < 0) {
469                         return err;
470                 }
471         }
472
473         return 0;
474 }
475
476 LOCAL W submitutil_setvolatilecookie(cookiedb_t *db, UB *host, W host_len, STIME time, UB *attr, W attr_len, UB *name, W name_len)
477 {
478         cookiedb_readheadercontext_t *ctx;
479         W i, err;
480
481         ctx = cookiedb_startheaderread(db, host, host_len, "/", 1, time);
482         if (ctx == NULL) {
483                 return -1; /* TODO */
484         }
485
486         for (i = 0; i < attr_len; i++) {
487                 err = cookiedb_readheadercontext_appendchar_attr(ctx, attr[i]);
488                 if (err < 0) {
489                         cookiedb_endheaderread(db, ctx);
490                         return err;
491                 }
492         }
493         err = cookiedb_readheadercontext_appendchar_name(ctx, '"');
494         if (err < 0) {
495                 cookiedb_endheaderread(db, ctx);
496                 return err;
497         }
498         for (i = 0; i < name_len; i++) {
499                 err = cookiedb_readheadercontext_appendchar_name(ctx, name[i]);
500                 if (err < 0) {
501                         cookiedb_endheaderread(db, ctx);
502                         return err;
503                 }
504         }
505         err = cookiedb_readheadercontext_appendchar_name(ctx, '"');
506         if (err < 0) {
507                 cookiedb_endheaderread(db, ctx);
508                 return err;
509         }
510
511         cookiedb_endheaderread(db, ctx);
512
513         return 0;
514 }
515
516 EXPORT W submitutil_setnamemailcookie(cookiedb_t *db, UB *host, W host_len, STIME time, UB *name, W name_len, UB *mail, W mail_len)
517 {
518         W err;
519
520         err = submitutil_setvolatilecookie(db, host, host_len, time, "NAME", 4, name, name_len);
521         if (err < 0) {
522                 return err;
523         }
524         err = submitutil_setvolatilecookie(db, host, host_len, time, "MAIL", 4, mail, mail_len);
525         if (err < 0) {
526                 return err;
527         }
528
529         return 0;
530 }
531
532 LOCAL W submitutil_makecookieheader(UB **str, W *len, cookiedb_t *cdb, UB *host, W host_len, UB *path, W path_len, STIME time)
533 {
534         cookiedb_writeheadercontext_t *ctx;
535         Bool cont;
536         UB *cstr;
537         W cstr_len, err;
538
539         ctx = cookiedb_startheaderwrite(cdb, host, host_len, path, path_len, False, time);
540         if (ctx == NULL) {
541                 return -1; /* TODO */
542         }
543
544         err = 0;
545         for (;;) {
546                 cont = cookiedb_writeheadercontext_makeheader(ctx, &cstr, &cstr_len);
547                 if (cont == False) {
548                         break;
549                 }
550
551                 err = sjstring_appendasciistring(str, len, cstr, cstr_len);
552                 if (err < 0) {
553                         break;
554                 }
555         }
556         cookiedb_endheaderwrite(cdb, ctx);
557
558         return err;
559 }
560
561 EXPORT W submitutil_makeheaderstring(UB *host, W host_len, UB *board, W board_len, UB *thread, W thread_len, W content_length, STIME time, cookiedb_t *cdb, UB **header, W *header_len)
562 {
563         UB *str = NULL;
564         W len = 0;
565         W err;
566
567         MAKEHEADER_ERR(&str, &len, "POST /test/bbs.cgi HTTP/1.1\r\n");
568         MAKEHEADER_ERR(&str, &len, "Host: ");
569         MAKEHEADER_ERR_LEN(&str, &len, host, host_len);
570         MAKEHEADER_ERR(&str, &len, "\r\n");
571         MAKEHEADER_ERR(&str, &len, "Accept: */*\r\n");
572         MAKEHEADER_ERR(&str, &len, "Referer: http://");
573         MAKEHEADER_ERR_LEN(&str, &len, host, host_len);
574         MAKEHEADER_ERR(&str, &len, "/test/read.cgi/");
575         MAKEHEADER_ERR_LEN(&str, &len, board, board_len);
576         MAKEHEADER_ERR(&str, &len, "/");
577         MAKEHEADER_ERR_LEN(&str, &len, thread, thread_len);
578         MAKEHEADER_ERR(&str, &len, "/\r\n");
579         MAKEHEADER_ERR(&str, &len, "Content-Length: ");
580         MAKEHEADER_NUM_ERR(&str, &len, content_length);
581         MAKEHEADER_ERR(&str, &len, "\r\n");
582         err = submitutil_makecookieheader(&str, &len, cdb, host, host_len, "/test/bbs.cgi", strlen("/test/bbs.cgi"), time);
583         if (err < 0) {
584                 return err;
585         }
586         MAKEHEADER_ERR(&str, &len, "Content-Type: application/x-www-form-urlencoded\r\n");
587         MAKEHEADER_ERR(&str, &len, "Accept-Language: ja\r\nUser-Agent: Monazilla/1.00 (bchan/0.301)\r\nConnection: close\r\n\r\n");
588
589         *header = str;
590         *header_len = len;
591
592         return 0;
593 }
594
595
596 LOCAL UB* submitutil_makeerrormessage_search_b_elm(UB *body, W body_len)
597 {
598         UB *ptr;
599         W i;
600
601         for (i = 0; i < body_len - 2; i++) {
602                 if (body[i] != '<') { /* tmp */
603                         continue;
604                 }
605                 if ((body[i+1] == 'b')||(body[i+1] == 'B')) {
606                         if (body[i+2] == '>') {
607                                 return body + i + 3;
608                         }
609                         if (body[i+2] == ' ') {
610                                 ptr = sjstring_searchchar(body + i, body_len - i, '>');
611                                 if (ptr == NULL) {
612                                         return NULL;
613                                 }
614                                 return ptr + 1;
615                         }
616                 }
617         }
618
619         return NULL;
620 }
621
622 EXPORT W submitutil_makeerrormessage(UB *body, W body_len, TC **msg, W *msg_len)
623 {
624         UB *ptr, *start, *end;
625         TC *ret;
626         W rem_len, ret_len;
627
628         *msg = NULL;
629         *msg_len = 0;
630
631         ptr = submitutil_makeerrormessage_search_b_elm(body, body_len);
632         if (ptr == NULL) {
633                 return 0; /* TODO */
634         }
635         start = ptr;
636         rem_len = start - body;
637
638         end = sjstring_searchchar(start, rem_len, '<');
639         if (end == NULL) {
640                 return 0; /* TODO */
641         }
642
643         ret_len = sjstring_totcs(start, end - start, NULL);
644         if (ret_len < 0) {
645                 return 0;
646         }
647
648         ret = malloc(sizeof(TC)*ret_len);
649         if (ret == NULL) {
650                 return -1; /* TODO */
651         }
652         sjstring_totcs(start, end - start, ret);
653
654         *msg = ret;
655         *msg_len = ret_len;
656
657         return 0;
658 }