1 /* ====================================================================
2  * The Kannel Software License, Version 1.0
3  *
4  * Copyright (c) 2001-2014 Kannel Group
5  * Copyright (c) 1998-2001 WapIT Ltd.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  *    if any, must include the following acknowledgment:
22  *       "This product includes software developed by the
23  *        Kannel Group (http://www.kannel.org/)."
24  *    Alternately, this acknowledgment may appear in the software itself,
25  *    if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Kannel" and "Kannel Group" must not be used to
28  *    endorse or promote products derived from this software without
29  *    prior written permission. For written permission, please
30  *    contact org@kannel.org.
31  *
32  * 5. Products derived from this software may not be called "Kannel",
33  *    nor may "Kannel" appear in their name, without prior written
34  *    permission of the Kannel Group.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED.  IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS
40  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
41  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
42  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
43  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
44  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
45  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
46  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Kannel Group.  For more information on
51  * the Kannel Group, please see <http://www.kannel.org/>.
52  *
53  * Portions of this software are based upon software originally written at
54  * WapIT Ltd., Helsinki, Finland for the Kannel project.
55  */
56 
57 /*
58  * A very simple push initiator for testing a push proxy gateway
59  *
60  * Read pap control content and push content from files, pack them into a PAP
61  * protocol MIME message and invoke push services specified by an url. Use a
62  * hardcoded message boundary (asdlfkjiurwgasf), for simpler command line
63  * interface.
64  * Repetitions and use of multiple threads can be requested, in addition of
65  * setting of some headers.
66  *
67  * By Aarno Syv�nen for Wiral Ltd and Global Networks Inc.
68  */
69 
70 #define MAX_THREADS 1024
71 #define MAX_IN_QUEUE 128
72 
73 #include <unistd.h>
74 #include <stdlib.h>
75 #include <string.h>
76 
77 #include "gwlib/gwlib.h"
78 #include "gw/wap_push_pap_compiler.h"
79 
80 static long max_pushes = 1;
81 static int verbose = 1,
82            use_hardcoded = 0,
83            num_urls = 0,
84            use_headers = 0,
85            use_config = 0,
86            accept_binary = 0,
87            use_numeric = 0,
88            use_string = 0,
89            use_content_header = 0,
90            add_epilogue = 0,
91            add_preamble = 0,
92            use_dlr_mask = 0,
93            use_dlr_url = 0;
94 static double wait_seconds = 0.0;
95 static Counter *counter = NULL;
96 static char **push_data = NULL;
97 static char *boundary = NULL;
98 static Octstr *content_flag = NULL;
99 static Octstr *appid_flag = NULL;
100 static Octstr *appid_string = NULL;
101 static Octstr *content_header = NULL;
102 static Octstr *content_transfer_encoding = NULL;
103 static Octstr *connection = NULL;
104 static Octstr *delimiter = NULL;
105 static Octstr *initiator_uri = NULL;
106 static Octstr *dlr_mask = NULL;
107 static Octstr *dlr_url = NULL;
108 
109 enum { SSL_CONNECTION_OFF = 0,
110        DEFAULT_NUMBER_OF_RELOGS = 2};
111 
112 /*
113  * Configuration variables
114  */
115 static int pi_ssl = SSL_CONNECTION_OFF;
116 static long retries = DEFAULT_NUMBER_OF_RELOGS;
117 static Octstr *ssl_client_certkey_file = NULL;
118 static Octstr *push_url = NULL;
119 static Octstr *pap_file = NULL;
120 static Octstr *content_file = NULL;
121 static Octstr *username = NULL;
122 static Octstr *password = NULL;
123 
read_test_ppg_config(Octstr * name)124 static void read_test_ppg_config(Octstr *name)
125 {
126     Cfg *cfg;
127     CfgGroup *grp;
128 
129     cfg = cfg_create(name);
130     if (cfg_read(cfg) == -1)
131         panic(0, "Cannot read a configuration file %s, exiting",
132               octstr_get_cstr(name));
133     cfg_dump(cfg);
134     grp = cfg_get_single_group(cfg, octstr_imm("test-ppg"));
135     cfg_get_integer(&retries, grp, octstr_imm("retries"));
136     cfg_get_bool(&pi_ssl, grp, octstr_imm("pi-ssl"));
137 #ifdef HAVE_LIBSSL
138     if (pi_ssl) {
139         ssl_client_certkey_file = cfg_get(grp,
140             octstr_imm("ssl-client-certkey-file"));
141         if (ssl_client_certkey_file != NULL) {
142             use_global_client_certkey_file(ssl_client_certkey_file);
143         } else {
144             error(0, "cannot set up SSL without client certkey file");
145             exit(1);
146         }
147     }
148 #endif
149 
150     grp = cfg_get_single_group(cfg, octstr_imm("configuration"));
151     push_url = cfg_get(grp, octstr_imm("push-url"));
152     pap_file =  cfg_get(grp, octstr_imm("pap-file"));
153     content_file =  cfg_get(grp, octstr_imm("content-file"));
154     if (!use_hardcoded) {
155         username = cfg_get(grp, octstr_imm("username"));
156         password = cfg_get(grp, octstr_imm("password"));
157     }
158 
159     cfg_destroy(cfg);
160 }
161 
add_delimiter(Octstr ** content)162 static void add_delimiter(Octstr **content)
163 {
164     if (octstr_compare(delimiter, octstr_imm("crlf")) == 0) {
165         octstr_format_append(*content, "%c", '\r');
166     }
167 
168     octstr_format_append(*content, "%c", '\n');
169 }
170 
add_push_application_id(Octstr * appid_flag,Octstr ** content,int use_string)171 static void add_push_application_id(Octstr *appid_flag, Octstr **content,
172                                     int use_string)
173 {
174     if (use_string) {
175         *content = octstr_format("%S\r\n", appid_string);
176         return;
177     }
178 
179     if (octstr_compare(appid_flag, octstr_imm("any")) == 0) {
180         if (!use_numeric)
181         	*content = octstr_create("X-WAP-Application-Id: http://www.wiral.com:*\r\n");
182         else
183             *content = octstr_create("X-WAP-Application-Id: 0\r\n");
184     } else if (octstr_compare(appid_flag, octstr_imm("ua")) == 0) {
185         if (!use_numeric)
186         	*content = octstr_create("X-WAP-Application-Id: http://www.wiral.com:wml.ua\r\n");
187         else
188         	*content = octstr_create("X-WAP-Application-Id: 2\r\n");
189     } else if (octstr_compare(appid_flag, octstr_imm("mms")) == 0) {
190         if (!use_numeric)
191         	*content = octstr_create("X-WAP-Application-Id: mms.ua\r\n");
192         else
193         	*content = octstr_create("X-WAP-Application-Id: 4\r\n");
194     } else if (octstr_compare(appid_flag, octstr_imm("scrap")) == 0) {
195         if (!use_numeric)
196         	*content = octstr_create("X-WAP-Application-Id: no appid at all\r\n");
197         else
198         	*content = octstr_create("X-WAP-Application-Id: this is not a numeric header\r\n");
199     }
200 }
201 
add_dlr_mask(List ** push_headers,Octstr * value)202 static void add_dlr_mask(List **push_headers, Octstr *value)
203 {
204     http_header_add(*push_headers, "X-Kannel-DLR-Mask",
205                     octstr_get_cstr(value));
206 }
207 
add_dlr_url(List ** push_headers,Octstr * value)208 static void add_dlr_url(List **push_headers, Octstr *value)
209 {
210     http_header_add(*push_headers, "X-Kannel-DLR-Url",
211                     octstr_get_cstr(value));
212 }
213 
add_part_header(Octstr * content_keader,Octstr ** wap_content)214 static void add_part_header(Octstr *content_keader, Octstr **wap_content)
215 {
216     if (use_content_header) {
217         octstr_append(*wap_content, content_header);
218     }
219 
220     add_delimiter(wap_content);
221 }
222 
223 
add_content_type(Octstr * content_flag,Octstr ** content)224 static void add_content_type(Octstr *content_flag, Octstr **content)
225 {
226 	if (*content == NULL)
227 		*content = octstr_create("");
228 
229     if (octstr_compare(content_flag, octstr_imm("wml")) == 0)
230     	octstr_append_cstr(*content, "Content-Type: text/vnd.wap.wml\r\n");
231     else if (octstr_compare(content_flag, octstr_imm("si")) == 0)
232 	    octstr_append_cstr(*content, "Content-Type: text/vnd.wap.si\r\n");
233     else if (octstr_compare(content_flag, octstr_imm("sl")) == 0)
234 	    octstr_append_cstr(*content, "Content-Type: text/vnd.wap.sl\r\n");
235     else if (octstr_compare(content_flag, octstr_imm("multipart")) == 0)
236         octstr_append_cstr(*content, "Content-Type: multipart/related; boundary=fsahgwruijkfldsa\r\n");
237     else if (octstr_compare(content_flag, octstr_imm("mms")) == 0)
238         octstr_append_cstr(*content, "Content-Type: application/vnd.wap.mms-message\r\n");
239 }
240 
add_content_transfer_encoding_type(Octstr * content_flag,Octstr * wap_content)241 static void add_content_transfer_encoding_type(Octstr *content_flag,
242                                                Octstr *wap_content)
243 {
244     if (!content_flag)
245 	return;
246 
247     if (octstr_compare(content_flag, octstr_imm("base64")) == 0)
248 	octstr_append_cstr(wap_content, "Content-transfer-encoding: base64");
249 
250     add_delimiter(&wap_content);
251 }
252 
add_connection_header(List ** push_headers,Octstr * connection)253 static void add_connection_header(List **push_headers, Octstr *connection)
254 {
255     if (!connection)
256         return;
257 
258     if (octstr_compare(connection, octstr_imm("close")) == 0)
259         http_header_add(*push_headers, "Connection", "close");
260     else if (octstr_compare(connection, octstr_imm("keep-alive")) == 0)
261         http_header_add(*push_headers, "Connection", "keep-alive");
262 }
263 
transfer_encode(Octstr * cte,Octstr * content)264 static void transfer_encode (Octstr *cte, Octstr *content)
265 {
266     if (!cte)
267 	return;
268 
269     if (octstr_compare(cte, octstr_imm("base64")) == 0) {
270        octstr_binary_to_base64(content);
271     }
272 }
273 
274 
275 /*
276  * Add boundary value to the multipart header.
277  */
make_multipart_value(const char * boundary)278 static Octstr *make_multipart_value(const char *boundary)
279 {
280     Octstr *hos;
281 
282     hos = octstr_format("%s", "multipart/related; boundary=");
283     octstr_append(hos, octstr_imm(boundary));
284     octstr_append(hos, octstr_imm("; type=\"application/xml\""));
285 
286     return hos;
287 }
288 
make_part_delimiter(Octstr * boundary)289 static Octstr *make_part_delimiter(Octstr *boundary)
290 {
291     Octstr *part_delimiter;
292 
293     part_delimiter = octstr_create("");
294     add_delimiter(&part_delimiter);
295     octstr_format_append(part_delimiter, "%s", "--");
296     octstr_append(part_delimiter, boundary);
297     add_delimiter(&part_delimiter);
298 
299     return part_delimiter;
300 }
301 
make_close_delimiter(Octstr * boundary)302 static Octstr *make_close_delimiter(Octstr *boundary)
303 {
304     Octstr *close_delimiter;
305 
306     close_delimiter = octstr_create("");
307     add_delimiter(&close_delimiter);
308     octstr_format_append(close_delimiter, "%s", "--");
309     octstr_append(close_delimiter, boundary);
310     octstr_format_append(close_delimiter, "%s", "--");
311     /*add_delimiter(&close_delimiter);*/
312 
313     return close_delimiter;
314 }
315 
push_headers_create(size_t content_len)316 static List *push_headers_create(size_t content_len)
317 {
318     List *push_headers;
319     Octstr *mos;
320 
321     mos = NULL;
322     push_headers = http_create_empty_headers();
323     if (use_hardcoded)
324         http_header_add(push_headers, "Content-Type", "multipart/related;"
325                         " boundary=asdlfkjiurwgasf; type=\"application/xml\"");
326     else
327         http_header_add(push_headers, "Content-Type",
328                         octstr_get_cstr(mos = make_multipart_value(boundary)));
329     if (use_headers)
330         http_add_basic_auth(push_headers, username, password);
331     add_connection_header(&push_headers, connection);
332     if (use_dlr_mask)
333         add_dlr_mask(&push_headers, dlr_mask);
334     if (use_dlr_url)
335         add_dlr_url(&push_headers, dlr_url);
336 
337     octstr_destroy(mos);
338 
339     /* add initiator... */
340     if (initiator_uri)
341 	http_header_add(push_headers, "X-Wap-Initiator-URI",
342 			octstr_get_cstr(initiator_uri));
343 
344     return push_headers;
345 }
346 
push_content_create(void)347 static Octstr *push_content_create(void)
348 {
349     Octstr *push_content,
350            *wap_content;
351     Octstr *wap_file_content,
352            *pap_content,
353            *pap_file_content,
354            *bpos,
355            *bcos;
356 
357     wap_content = NULL;
358     push_content = NULL;
359     if (use_hardcoded) {
360         push_content = octstr_create("\r\n\r\n"
361                   "--asdlfkjiurwgasf\r\n"
362                   "Content-Type: application/xml\r\n\r\n"
363                   "<?xml version=\"1.0\"?>"
364                   "<!DOCTYPE pap PUBLIC \"-//WAPFORUM//DTD PAP//EN\""
365                              " \"http://www.wapforum.org/DTD/pap_1.0.dtd\">"
366                   "<pap>"
367                         "<push-message push-id=\"9fjeo39jf084@pi.com\""
368                           " deliver-before-timestamp=\"2002-11-01T06:45:00Z\""
369                           " deliver-after-timestamp=\"2000-02-27T06:45:00Z\""
370                           " progress-notes-requested=\"false\">"
371 			     "<address address-value=\"WAPPUSH=+358408676001/"
372 			 	"TYPE=PLMN@ppg.carrier.com\">"
373                              "</address>"
374                              "<quality-of-service"
375                                " priority=\"low\""
376                                " delivery-method=\"unconfirmed\""
377                                " network-required=\"true\""
378                                " network=\"GSM\""
379                                " bearer-required=\"true\""
380                                " bearer=\"SMS\">"
381                              "</quality-of-service>"
382                         "</push-message>"
383                   "</pap>\r\n\r\n"
384                   "--asdlfkjiurwgasf\r\n"
385                   "Content-Type: text/vnd.wap.si\r\n\r\n"
386                   "<?xml version=\"1.0\"?>"
387                   "<!DOCTYPE si PUBLIC \"-//WAPFORUM//DTD SI 1.0//EN\" "
388                     " \"http://www.wapforum.org/DTD/si.dtd\">"
389                   "<si>"
390                       "<indication href=\"http://wap.iobox.fi\""
391                           " si-id=\"1@wiral.com\""
392                           " action=\"signal-high\""
393                           " created=\"1999-06-25T15:23:15Z\""
394                           " si-expires=\"2002-12-30T00:00:00Z\">"
395                           "Want to test a fetch?"
396                       "</indication>"
397                    "</si>\r\n\r\n"
398                  "--asdlfkjiurwgasf--\r\n\r\n"
399                  "");
400     } else {
401         add_push_application_id(appid_flag, &wap_content, use_string);
402         add_content_type(content_flag, &wap_content);
403         add_content_transfer_encoding_type(content_transfer_encoding,
404                                            wap_content);
405         add_part_header(content_header, &wap_content);
406 
407         /* Read the content file. (To be pushed)*/
408         if ((wap_file_content =
409                 octstr_read_file(octstr_get_cstr(content_file))) == NULL)
410 	         panic(0, "Stopping");
411         if (accept_binary) {
412             octstr_delete_matching(wap_file_content, octstr_imm(" "));
413             octstr_delete_matching(wap_file_content, octstr_imm("\n"));
414             octstr_delete_matching(wap_file_content, octstr_imm("\r"));
415             if (!octstr_is_all_hex(wap_file_content))
416                 panic(0, "non-hex chars in the content file, cannot continue");
417             octstr_hex_to_binary(wap_file_content);
418         }
419 
420 	transfer_encode(content_transfer_encoding, wap_file_content);
421         octstr_append(wap_content, wap_file_content);
422         octstr_destroy(wap_file_content);
423 
424         /* Read the control file. (To control pushing)*/
425         pap_content = octstr_format("%s", "Content-Type: application/xml");
426         add_delimiter(&pap_content);
427         add_delimiter(&pap_content);
428         if ((pap_file_content =
429                 octstr_read_file(octstr_get_cstr(pap_file))) ==  NULL)
430 	        panic(0, "Stopping");
431 
432         octstr_append(pap_content, pap_file_content);
433         octstr_destroy(pap_file_content);
434 
435         if (wap_content == NULL || pap_content == NULL)
436 	        panic(0, "Cannot open the push content files");
437 
438         push_content = octstr_create("");
439         if (add_preamble)
440             octstr_append(push_content, octstr_imm("the parser should discard this"));
441         octstr_append(push_content,
442             bpos = make_part_delimiter(octstr_imm(boundary)));
443         /*octstr_append(push_content, octstr_imm("\r\n"));*/ /* Do we accept an additional
444                                                           * clrf ? */
445         octstr_append(push_content, pap_content);
446         octstr_append(push_content, bpos);
447         octstr_destroy(bpos);
448         octstr_append(push_content, wap_content);
449         octstr_append(push_content,
450             bcos = make_close_delimiter(octstr_imm(boundary)));
451         if (add_epilogue) {
452             octstr_append(push_content, octstr_imm("\r\n"));
453             octstr_append(push_content, octstr_imm("the parser should discard this"));
454         }
455         octstr_destroy(bcos);
456         octstr_destroy(pap_content);
457         octstr_destroy(wap_content);
458     }
459 
460     return push_content;
461 }
462 
make_url(Octstr ** url)463 static void make_url(Octstr **url)
464 {
465     if (use_config && !use_headers) {
466         octstr_append(*url, octstr_imm("?username="));
467         octstr_append(*url, username ? username : octstr_imm("default"));
468         octstr_append(*url, octstr_imm("&password="));
469         octstr_append(*url, password ? password: octstr_imm("default"));
470     }
471 }
472 
start_push(HTTPCaller * caller,long i)473 static void start_push(HTTPCaller *caller, long i)
474 {
475     List *push_headers;
476     Octstr *push_content;
477     long *id;
478 
479     push_content = push_content_create();
480     push_headers = push_headers_create(octstr_len(push_content));
481     if (verbose) {
482        debug("test.ppg", 0, "we have push content");
483        octstr_dump(push_content, 0);
484        debug("test.ppg", 0, "and headers");
485        http_header_dump(push_headers);
486     }
487 
488     id = gw_malloc(sizeof(long));
489     *id = i;
490     make_url(&push_url);
491     debug("test.ppg", 0, "TEST_PPG: starting to push job %ld", i);
492     http_start_request(caller, HTTP_METHOD_POST, push_url, push_headers,
493                        push_content, 0, id, ssl_client_certkey_file);
494     debug("test.ppg", 0, "push done");
495     octstr_destroy(push_content);
496     http_destroy_headers(push_headers);
497 }
498 
499 /*
500  * Try log in defined number of times, when got response 401 and authentica-
501  * tion info is in headers.
502  */
receive_push_reply(HTTPCaller * caller)503 static int receive_push_reply(HTTPCaller *caller)
504 {
505     void *id;
506     long *trid;
507     int http_status,
508         tries;
509     List *reply_headers;
510     Octstr *final_url,
511            *auth_url,
512            *reply_body,
513            *os,
514            *push_content,
515            *auth_reply_body;
516     WAPEvent *e;
517     List *retry_headers;
518 
519     http_status = HTTP_UNAUTHORIZED;
520     tries = 0;
521 
522     id = http_receive_result(caller, &http_status, &final_url, &reply_headers,
523                              &reply_body);
524 
525     if (id == NULL || http_status == -1 || final_url == NULL) {
526         error(0, "push failed, no reason found");
527         goto push_failed;
528     }
529 
530     while (use_headers && http_status == HTTP_UNAUTHORIZED && tries < retries) {
531         debug("test.ppg", 0, "try number %d", tries);
532         debug("test.ppg", 0, "authentication failure, get a challenge");
533         http_destroy_headers(reply_headers);
534         push_content = push_content_create();
535         retry_headers = push_headers_create(octstr_len(push_content));
536         http_add_basic_auth(retry_headers, username, password);
537         trid = gw_malloc(sizeof(long));
538         *trid = tries;
539         http_start_request(caller, HTTP_METHOD_POST, final_url, retry_headers,
540                            push_content, 0, trid, NULL);
541         debug("test.ppg ", 0, "TEST_PPG: doing response to %s",
542               octstr_get_cstr(final_url));
543 
544         octstr_destroy(push_content);
545         http_destroy_headers(retry_headers);
546 
547         trid = http_receive_result(caller, &http_status, &auth_url,
548                                    &reply_headers, &auth_reply_body);
549 
550         if (trid == NULL || http_status == -1 || auth_url == NULL) {
551             error(0, "unable to send authorisation, no reason found");
552             goto push_failed;
553         }
554 
555         debug("test.ppg", 0, "TEST_PPG: send authentication to %s, retry %ld",
556                octstr_get_cstr(auth_url), *(long *) trid);
557         gw_free(trid);
558         octstr_destroy(auth_reply_body);
559         octstr_destroy(auth_url);
560         ++tries;
561     }
562 
563     if (http_status == HTTP_NOT_FOUND) {
564         error(0, "push failed, service not found");
565         goto push_failed;
566     }
567 
568     if (http_status == HTTP_FORBIDDEN) {
569         error(0, "push failed, service forbidden");
570         goto push_failed;
571     }
572 
573     if (http_status == HTTP_UNAUTHORIZED) {
574         if (use_headers)
575             error(0, "tried %ld times, stopping", retries);
576         else
577 	        error(0, "push failed, authorisation failure");
578         goto push_failed;
579     }
580 
581     debug("test.ppg", 0, "TEST_PPG: push %ld done: reply from,  %s",
582           *(long *) id, octstr_get_cstr(final_url));
583     gw_free(id);
584     octstr_destroy(final_url);
585 
586     if (verbose)
587         debug("test.ppg", 0, "TEST_PPG: reply headers were");
588 
589     while ((os = gwlist_extract_first(reply_headers)) != NULL) {
590         if (verbose)
591             octstr_dump(os, 0);
592         octstr_destroy(os);
593     }
594 
595     if (verbose) {
596         debug("test.ppg", 0, "TEST_PPG: reply body was");
597         octstr_dump(reply_body, 0);
598     }
599 
600     e = NULL;
601     if (pap_compile(reply_body, &e) < 0) {
602         warning(0, "TEST_PPG: receive_push_reply: cannot compile pap message");
603         goto parse_error;
604     }
605 
606     switch (e->type) {
607         case Push_Response:
608 	        debug("test.ppg", 0, "TEST_PPG: and type push response");
609 	    break;
610 
611         case Bad_Message_Response:
612 	        debug("test.ppg", 0, "TEST_PPG: and type bad message response");
613         break;
614 
615         default:
616             warning(0, "TEST_PPG: unknown event received from %s",
617                     octstr_get_cstr(final_url));
618         break;
619     }
620 
621     octstr_destroy(reply_body);
622     wap_event_destroy(e);
623     http_destroy_headers(reply_headers);
624     return 0;
625 
626 push_failed:
627     gw_free(id);
628     octstr_destroy(final_url);
629     octstr_destroy(reply_body);
630     http_destroy_headers(reply_headers);
631     return -1;
632 
633 parse_error:
634     octstr_destroy(reply_body);
635     http_destroy_headers(reply_headers);
636     wap_event_destroy(e);
637     return -1;
638 }
639 
push_thread(void * arg)640 static void push_thread(void *arg)
641 {
642     HTTPCaller *caller;
643     long succeeded, failed, in_queue;
644     unsigned long i;
645 
646     caller = arg;
647     succeeded = 0;
648     failed = 0;
649     in_queue = 0;
650     i = 0;
651 
652     for (;;) {
653         while (in_queue < MAX_IN_QUEUE) {
654 	        i = counter_increase(counter);
655             if (i >= max_pushes)
656 	            goto receive_rest;
657             start_push(caller, i);
658             if (wait_seconds > 0)
659                 gwthread_sleep(wait_seconds);
660             ++in_queue;
661         }
662 
663         while (in_queue >= MAX_IN_QUEUE) {
664 	        if (receive_push_reply(caller) == -1)
665 	            ++failed;
666             else
667 	            ++succeeded;
668             --in_queue;
669         }
670     }
671 
672 receive_rest:
673     while (in_queue > 0) {
674         if (receive_push_reply(caller) == -1)
675 	        ++failed;
676         else
677 	        ++succeeded;
678         --in_queue;
679     }
680 
681     http_caller_destroy(caller);
682     info(0, "TEST_PPG: In thread %ld %ld succeeded, %ld failed",
683          (long) gwthread_self(), succeeded, failed);
684 }
685 
help(void)686 static void help(void)
687 {
688     info(0, "Usage: test_ppg [options] push_url [content_file pap_file]");
689     info(0, "      or");
690     info(0, "Usage: test_ppg [options] [conf_file]");
691     info(0, "Implements push initiator for wap push. Push services are ");
692     info(0, "located in push_url, push content in the file content file.");
693     info(0, "File pap_file contains pap control document that controls");
694     info(0, "pushing");
695     info(0, "If option -H is not used, command line has either three or one");
696     info(0, "arguments:");
697     info(0, "      a) the url of the push proxy gateway");
698     info(0, "      b) a file containing the content to be pushed");
699     info(0, "      c) a pap document controlling pushing");
700     info(0, "     or");
701     info(0, "      a) a test configuration file, containing all these");
702     info(0, "Option -H cannot be used with a configuration file. If it is");
703     info(0, "used, the push url is the only argument.");
704     info(0, "Options are:");
705     info(0, "-h");
706     info(0, "print this info");
707     info(0, "-c content qualifier");
708     info(0, "Define content type of the push content. Wml, multipart, nil,");
709     info(0, "scrap, sl, and si accepted. Si is default, nil (no content");
710     info(0, " type at all) and scrap (random string) are used for debugging");
711     info(0, "-a application id");
712     info(0, "Define the client application that will handle the push. Any,");
713     info(0, "ua, mms, nil and scrap accepted, default ua.");
714     info(0, "-n");
715     info(0, "if set, use numeric appid values instead of string ones. For");
716     info(0, "instance, '4' instead of 'mms.ua'. Default is off.");
717     info(0, "-s string");
718     info(0, "supply a message header as a plain string. For instance");
719     info(0, "-s x-wap-application-id:mms.ua equals -a ua. Default is");
720     info(0, "x-wap-application-id:mms.ua.");
721     info(0, "-I string");
722     info(0, "supply an initiator header as a plain string. For instance");
723     info(0, "-I x-wap-application-id:http://foo.bar equals -I http://foo.bar");
724     info(0, "-S string");
725     info(0, "supply an additional part header (for push content) as a string.");
726     info(0, "For instance, -S Content-Language: en. Default no additional part");
727     info(0, "headers.");
728     info(0, "-b");
729     info(0, "If true, send username/password in headers. Default false");
730     info(0, "-v number");
731     info(0, "    Set log level for stderr logging. Default 0 (debug)");
732     info(0, "-q");
733     info(0, "    Do not print debugging information");
734     info(0, "Default: print it");
735     info(0, "-r number");
736     info(0, "    Make `number' requests. Default one request");
737     info(0, "-i seconds");
738     info(0, "    Wait 'seconds' seconds between pushes. Default: do not wait");
739     info(0, "-e transfer encoding");
740     info(0, "    use transfer encoding to send push contents.");
741     info(0, "    Currently supported is base64.");
742     info(0, "-k connection header");
743     info(0, "Use the connection header. Keep-alive and close accepted,");
744     info(0, "default close");
745     info(0, "-H");
746     info(0, "Use hardcoded MIME message, containing a pap control document.");
747     info(0, "In addition, use hardcoded username/password in headers (if ");
748     info(0, "flag -b is set, too");
749     info(0, "Default: read components from files");
750     info(0, "-t");
751     info(0, "number of threads, maximum 1024, default 1");
752     info(0, "-B");
753     info(0, "accept binary push content. Default: off.");
754     info(0, "Binary content consist of hex numbers. In addition, crs, lfs and");
755     info(0, "spaces are accepted, and ignored.");
756     info(0, "-d value");
757     info(0, "set delimiter to be used. Accepted values crlf and lf. Default crlf.");
758     info(0, "-E");
759     info(0, "If set, add a hardcoded epilogue (epilogue is to be discarded anyway).");
760     info(0, "Default off.");
761     info(0, "-p");
762     info(0, "If set, add hardcoded preamble. Default is off.");
763     info(0, "-m value");
764     info(0, "If set, add push header X-Kannel-DLR-Mask: value");
765     info(0, "Default off.");
766     info(0, "-u value");
767     info(0, "If set, add push header X-Kannel-DLR-Url: value");
768     info(0, "Default off.");
769 }
770 
main(int argc,char ** argv)771 int main(int argc, char **argv)
772 {
773     int opt,
774         num_threads;
775     time_t start,
776            end;
777     double run_time;
778     long threads[MAX_THREADS];
779     long i;
780     Octstr *fos;
781 
782     gwlib_init();
783     num_threads = 1;
784 
785     while ((opt = getopt(argc, argv, "HhBbnEpv:qr:t:c:a:i:e:k:d:s:S:I:m:u:")) != EOF) {
786         switch(opt) {
787 	    case 'v':
788 	        log_set_output_level(atoi(optarg));
789 	    break;
790 
791 	    case 'q':
792 	        verbose = 0;
793 	    break;
794 
795 	    case 'r':
796 	        max_pushes = atoi(optarg);
797 	    break;
798 
799 	    case 'i':
800                 wait_seconds = atof(optarg);
801 	    break;
802 
803             case 't':
804 	        num_threads = atoi(optarg);
805                 if (num_threads > MAX_THREADS)
806 		    num_threads = MAX_THREADS;
807 	    break;
808 
809 	    case 'H':
810 	        use_hardcoded = 1;
811 	    break;
812 
813 	    case 'c':
814 	        content_flag = octstr_create(optarg);
815             if (octstr_compare(content_flag, octstr_imm("wml")) != 0 &&
816                     octstr_compare(content_flag, octstr_imm("si")) != 0 &&
817                     octstr_compare(content_flag, octstr_imm("sl")) != 0 &&
818                     octstr_compare(content_flag, octstr_imm("nil")) != 0 &&
819                     octstr_compare(content_flag, octstr_imm("mms")) != 0 &&
820                     octstr_compare(content_flag, octstr_imm("scrap")) != 0 &&
821                     octstr_compare(content_flag, octstr_imm("multipart")) != 0) {
822 		        octstr_destroy(content_flag);
823 		        error(0, "TEST_PPG: Content type not known");
824 		        help();
825                          exit(1);
826             }
827 	    break;
828 
829 	    case 'a':
830 	        appid_flag = octstr_create(optarg);
831                 if (octstr_compare(appid_flag, octstr_imm("any")) != 0 &&
832                         octstr_compare(appid_flag, octstr_imm("ua")) != 0 &&
833                         octstr_compare(appid_flag, octstr_imm("mms")) != 0 &&
834                         octstr_compare(appid_flag, octstr_imm("nil")) != 0 &&
835                         octstr_compare(appid_flag, octstr_imm("scrap")) != 0) {
836 		octstr_destroy(appid_flag);
837 		error(0, "TEST_PPG: Push application id not known");
838 		help();
839                 exit(1);
840            }
841 	    break;
842 
843             case 'n':
844                 use_numeric = 1;
845             break;
846 
847             case 's':
848                 appid_string = octstr_create(optarg);
849                 use_string = 1;
850             break;
851 
852             case 'S':
853                 content_header = octstr_create(optarg);
854                 use_content_header = 1;
855             break;
856 
857 	    case 'e':
858 		content_transfer_encoding = octstr_create(optarg);
859                 if (octstr_compare(content_transfer_encoding, octstr_imm("base64")) != 0) {
860 	            octstr_destroy(content_transfer_encoding);
861 		    error(0, "TEST_PPG: unknown content transfer"
862                       " encoding \"%s\"", octstr_get_cstr(content_transfer_encoding));
863 		    help();
864                     exit(1);
865 		}
866 	    break;
867 
868 	    case 'k':
869 	        connection = octstr_create(optarg);
870                 if (octstr_compare(connection, octstr_imm("close")) != 0 &&
871                         octstr_compare(connection, octstr_imm("keep-alive")) != 0) {
872 	            octstr_destroy(connection);
873 		    error(0, "TEST_PPG: Connection-header unacceptable");
874 		    help();
875                     exit(1);
876                 }
877 	    break;
878 
879 	    case 'h':
880 	        help();
881             exit(1);
882 
883 	    case 'b':
884 	        use_headers = 1;
885 	    break;
886 
887             case 'B':
888                 accept_binary = 1;
889             break;
890 
891             case 'd':
892                 delimiter = octstr_create(optarg);
893                 if (octstr_compare(delimiter, octstr_imm("crlf")) != 0 &&
894                         octstr_compare(delimiter, octstr_imm("lf")) != 0) {
895                     octstr_destroy(delimiter);
896                     error(0, "illegal d value");
897                     help();
898                     exit(1);
899                 }
900             break;
901 
902             case 'E':
903                 add_epilogue = 1;
904             break;
905 
906             case 'p':
907                 add_preamble = 1;
908             break;
909 
910             case 'I':
911                 initiator_uri = octstr_create(optarg);
912 		break;
913 
914             case 'm':
915                 use_dlr_mask = 1;
916                 dlr_mask = octstr_create(optarg);
917             break;
918 
919             case 'u':
920                 use_dlr_url = 1;
921                 dlr_url = octstr_create(optarg);
922             break;
923 
924 	case '?':
925 	    default:
926 	        error(0, "TEST_PPG: Invalid option %c", opt);
927             help();
928             error(0, "Stopping");
929             exit(1);
930         }
931     }
932 
933     if (optind == argc) {
934         help();
935         exit(1);
936     }
937 
938     push_data = argv + optind;
939     num_urls = argc - optind;
940 
941     if (content_flag == NULL)
942         content_flag = octstr_imm("si");
943 
944     if (appid_flag == NULL)
945         appid_flag = octstr_imm("ua");
946 
947     if (appid_string == NULL)
948         appid_string = octstr_imm("x-wap-application-id: wml.ua");
949 
950     if (content_header == NULL)
951         use_content_header = 0;
952 
953     if (dlr_mask == NULL)
954         use_dlr_mask = 0;
955 
956     if (dlr_url == NULL)
957         use_dlr_url = 0;
958 
959     if (delimiter == NULL)
960         delimiter = octstr_imm("crlf");
961 
962     if (use_hardcoded) {
963         username = octstr_imm("troo");
964         password = octstr_imm("far");
965     }
966 
967     if (push_data[0] == NULL) {
968         error(0, "No ppg address or config file, stopping");
969         exit(1);
970     }
971 
972     use_config = 0;
973     if (!use_hardcoded) {
974         if (push_data[1] == NULL) {
975             info(0, "a configuration file input assumed");
976             read_test_ppg_config(fos = octstr_format("%s", push_data[0]));
977             octstr_destroy(fos);
978             use_config = 1;
979         }
980     }
981 
982     if (!use_config)
983         push_url = octstr_format("%s", push_data[0]);
984 
985     if (!use_hardcoded && !use_config && push_data[1] != NULL) {
986         if (push_data[2] == NULL) {
987 	        error(0, "no pap control document, stopping");
988             exit(1);
989         } else {
990            info(0, "an input without a configuration file assumed");
991            content_file = octstr_create(push_data[1]);
992            pap_file = octstr_create(push_data[2]);
993            debug("test.ppg", 0, "using %s as a content file", push_data[1]);
994            debug("test.ppg", 0, "using %s as a control file", push_data[2]);
995         }
996     }
997 
998     boundary = "asdlfkjiurwghasf";
999     counter = counter_create();
1000 
1001     time(&start);
1002     if (num_threads == 0)
1003         push_thread(http_caller_create());
1004     else {
1005         for (i = 0; i < num_threads; ++i)
1006 	        threads[i] = gwthread_create(push_thread, http_caller_create());
1007 	    for (i = 0; i < num_threads; ++i)
1008 	        gwthread_join(threads[i]);
1009     }
1010     time(&end);
1011     run_time = difftime(end, start);
1012     info(0, "TEST_PPG: %ld requests in %f seconds, %f requests per second",
1013          max_pushes, run_time, max_pushes / run_time);
1014 
1015     octstr_destroy(content_flag);
1016     octstr_destroy(appid_flag);
1017     octstr_destroy(content_header);
1018     octstr_destroy(content_file);
1019     octstr_destroy(pap_file);
1020     octstr_destroy(ssl_client_certkey_file);
1021     octstr_destroy(username);
1022     octstr_destroy(password);
1023     octstr_destroy(push_url);
1024     octstr_destroy(connection);
1025     octstr_destroy(delimiter);
1026     octstr_destroy(dlr_mask);
1027     octstr_destroy(dlr_url);
1028     counter_destroy(counter);
1029     gwlib_shutdown();
1030 
1031     exit(0);
1032 }
1033 
1034 
1035 
1036 
1037 
1038 
1039 
1040 
1041 
1042