Merge branch 'experimental' of git://github.com/Dieterbe/uzbl into experimental
[uzbl-mobile] / uzbl.c
1 /* -*- c-basic-offset: 4; */
2 // Original code taken from the example webkit-gtk+ application. see notice below.
3 // Modified code is licensed under the GPL 3.  See LICENSE file.
4
5
6 /*
7  * Copyright (C) 2006, 2007 Apple Inc.
8  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
20  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32
33 #define LENGTH(x) (sizeof x / sizeof x[0])
34 #define MAX_BINDINGS 256
35
36 #include <gtk/gtk.h>
37 #include <gdk/gdkx.h>
38 #include <gdk/gdkkeysyms.h>
39 #include <sys/socket.h>
40 #include <sys/stat.h>
41 #include <sys/types.h>
42 #include <sys/un.h>
43 #include <webkit/webkit.h>
44 #include <pthread.h>
45 #include <stdio.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include <stdlib.h>
49 #include <errno.h>
50 #include <string.h>
51 #include <fcntl.h>
52 #include <sys/socket.h>
53 #include <sys/un.h>
54 #include <libsoup/soup.h>
55 #include "uzbl.h"
56
57 /* housekeeping / internal variables */
58 static GtkWidget*     main_window;
59 static GtkWidget*     mainbar;
60 static GtkWidget*     mainbar_label;
61 static GtkScrollbar*  scbar_v;   // Horizontal and Vertical Scrollbar 
62 static GtkScrollbar*  scbar_h;   // (These are still hidden)
63 static GtkAdjustment* bar_v; // Information about document length
64 static GtkAdjustment* bar_h; // and scrolling position
65 static WebKitWebView* web_view;
66 static gchar*         main_title;
67 static gchar          selected_url[500] = "\0";
68 static gint           load_progress;
69 static Window         xwin = 0;
70 static char           fifo_path[64];
71 static char           socket_path[108];
72 static char           executable_path[500];
73 static GString*       keycmd;
74
75 /* state variables (initial values coming from command line arguments but may be changed later) */
76 static gchar*   uri         = NULL;
77 static gchar*   config_file = NULL;
78 static gchar    config_file_path[500];
79 static gboolean verbose     = FALSE;
80
81 /* settings from config: group behaviour */
82 static gchar*   history_handler    = NULL;
83 static gchar*   fifo_dir           = NULL;
84 static gchar*   socket_dir         = NULL;
85 static gchar*   download_handler   = NULL;
86 static gboolean always_insert_mode = FALSE;
87 static gboolean show_status        = FALSE;
88 static gboolean insert_mode        = FALSE;
89 static gboolean status_top         = FALSE;
90 static gchar*   modkey             = NULL;
91 static guint    modmask            = 0;
92 static guint    http_debug         = 0;
93 static gdouble   hscroll            = 20;
94 static gdouble   vscroll            = 20;
95
96 /* settings from config: group bindings, key -> action */
97 static GHashTable* bindings;
98
99 /* command list: name -> Command  */
100 static GHashTable* commands;
101
102 /* commandline arguments (set initial values for the state variables) */
103 static GOptionEntry entries[] =
104 {
105     { "uri",     'u', 0, G_OPTION_ARG_STRING, &uri,         "Uri to load", NULL },
106     { "verbose", 'v', 0, G_OPTION_ARG_NONE,   &verbose,     "Be verbose",  NULL },
107     { "config",  'c', 0, G_OPTION_ARG_STRING, &config_file, "Config file", NULL },
108     { NULL,      0, 0, 0, NULL, NULL, NULL }
109 };
110
111 typedef void (*Command)(WebKitWebView*, const char *);
112
113 /* XDG stuff */
114 static char *XDG_CONFIG_HOME_default[256];
115 static char *XDG_CONFIG_DIRS_default = "/etc/xdg";
116
117 /* libsoup stuff - proxy and friends; networking aptions actually */
118 static SoupSession *soup_session;
119 static SoupLogger *soup_logger;
120 static char *proxy_url = NULL;
121 static char *useragent = NULL;
122 static gint max_conns;
123 static gint max_conns_host;
124
125 /* --- CALLBACKS --- */
126
127 static gboolean
128 new_window_cb (WebKitWebView *web_view, WebKitWebFrame *frame, WebKitNetworkRequest *request, WebKitWebNavigationAction *navigation_action, WebKitWebPolicyDecision *policy_decision, gpointer user_data) {
129     (void) web_view;
130     (void) frame;
131     (void) navigation_action;
132     (void) policy_decision;
133     (void) user_data;
134     const gchar* uri = webkit_network_request_get_uri (request);
135     printf("New window requested -> %s \n", uri);
136     new_window_load_uri(uri);
137     return (FALSE);
138 }
139
140 WebKitWebView*
141 create_web_view_cb (WebKitWebView  *web_view, WebKitWebFrame *frame, gpointer user_data) {
142     (void) web_view;
143     (void) frame;
144     (void) user_data;
145     if (selected_url[0]!=0) {
146         printf("\nNew web view -> %s\n",selected_url);
147         new_window_load_uri(selected_url);
148     } else {
149         printf("New web view -> %s\n","Nothing to open, exiting");
150     }
151     return (NULL);
152 }
153
154 static gboolean
155 download_cb (WebKitWebView *web_view, GObject *download, gpointer user_data) {
156     (void) web_view;
157     (void) user_data;
158     if (download_handler) {
159         const gchar* uri = webkit_download_get_uri ((WebKitDownload*)download);
160         printf("Download -> %s\n",uri);
161         run_command(download_handler, uri);
162     }
163     return (FALSE);
164 }
165
166 /* scroll a bar in a given direction */
167 static void
168 scroll (double i, GtkAdjustment* bar) {
169     gtk_adjustment_set_value (bar, gtk_adjustment_get_value(bar)+i);
170 }
171
172 static void scroll_up (WebKitWebView* page, const char *param) {
173     (void) page;
174     (void) param;
175
176     scroll (-vscroll, bar_v);
177 }
178
179 static void scroll_left (WebKitWebView* page, const char *param) {
180     (void) page;
181     (void) param;
182
183     scroll (-hscroll, bar_h);
184 }
185
186 static void scroll_down (WebKitWebView* page, const char *param) {
187     (void) page;
188     (void) param;
189
190     scroll (vscroll, bar_v);
191 }
192
193 static void scroll_right (WebKitWebView* page, const char *param) {
194     (void) page;
195     (void) param;
196
197     scroll (hscroll, bar_h);
198 }
199
200 static void
201 toggle_status_cb (WebKitWebView* page, const char *param) {
202     (void)page;
203     (void)param;
204
205     if (show_status) {
206         gtk_widget_hide(mainbar);
207     } else {
208         gtk_widget_show(mainbar);
209     }
210     show_status = !show_status;
211     update_title();
212 }
213
214 static void
215 link_hover_cb (WebKitWebView* page, const gchar* title, const gchar* link, gpointer data) {
216     (void) page;
217     (void) title;
218     (void) data;    
219     //ADD HOVER URL TO WINDOW TITLE
220     selected_url[0] = '\0';
221     if (link) {
222         strcpy (selected_url, link);
223     }
224     update_title();
225 }
226
227 static void
228 title_change_cb (WebKitWebView* web_view, WebKitWebFrame* web_frame, const gchar* title, gpointer data) {
229     (void) web_view;
230     (void) web_frame;
231     (void) data;
232     if (main_title)
233         g_free (main_title);
234     main_title = g_strdup (title);
235     update_title();
236 }
237
238 static void
239 progress_change_cb (WebKitWebView* page, gint progress, gpointer data) {
240     (void) page;
241     (void) data;
242     load_progress = progress;
243     update_title();
244 }
245
246 static void
247 load_commit_cb (WebKitWebView* page, WebKitWebFrame* frame, gpointer data) {
248     (void) page;
249     (void) data;
250     free (uri);
251     GString* newuri = g_string_new (webkit_web_frame_get_uri (frame));
252     uri = g_string_free (newuri, FALSE);
253 }
254
255 static void
256 destroy_cb (GtkWidget* widget, gpointer data) {
257     (void) widget;
258     (void) data;
259     gtk_main_quit ();
260 }
261
262 static void
263 log_history_cb () {
264    if (history_handler) {
265        time_t rawtime;
266        struct tm * timeinfo;
267        char date [80];
268        time ( &rawtime );
269        timeinfo = localtime ( &rawtime );
270        strftime (date, 80, "%Y-%m-%d %H:%M:%S", timeinfo);
271        GString* args = g_string_new ("");
272        g_string_printf (args, "'%s'", date);
273        run_command(history_handler, args->str);
274        g_string_free (args, TRUE);
275    }
276 }
277
278 /* VIEW funcs (little webkit wrappers) */
279
280 #define VIEWFUNC(name) static void view_##name(WebKitWebView *page, const char *param){(void)param; webkit_web_view_##name(page);}
281 VIEWFUNC(reload)
282 VIEWFUNC(stop_loading)
283 VIEWFUNC(zoom_in)
284 VIEWFUNC(zoom_out)
285 VIEWFUNC(go_back)
286 VIEWFUNC(go_forward)
287 #undef VIEWFUNC
288
289 /* -- command to callback/function map for things we cannot attach to any signals */
290 // TODO: reload
291
292 static struct {char *name; Command command;} cmdlist[] =
293 {
294     { "back",          view_go_back       },
295     { "forward",       view_go_forward    },
296     { "scroll_down",   scroll_down        },
297     { "scroll_up",     scroll_up          },
298     { "scroll_left",   scroll_left        },
299     { "scroll_right",  scroll_right       },
300     { "reload",        view_reload,       }, //Buggy
301     { "refresh",       view_reload,       }, /* for convenience, will change */
302     { "stop",          view_stop_loading, },
303     { "zoom_in",       view_zoom_in,      }, //Can crash (when max zoom reached?).
304     { "zoom_out",      view_zoom_out,     },
305     { "uri",           load_uri           },
306     { "toggle_status", toggle_status_cb   },
307     { "spawn",         spawn              },
308     { "exit",          close_uzbl         },
309     { "insert_mode",   set_insert_mode    }
310 };
311
312 static void
313 commands_hash(void)
314 {
315     unsigned int i;
316     commands = g_hash_table_new(g_str_hash, g_str_equal);
317
318     for (i = 0; i < LENGTH(cmdlist); i++)
319         g_hash_table_insert(commands, cmdlist[i].name, cmdlist[i].command);
320 }
321
322 /* -- CORE FUNCTIONS -- */
323
324 void
325 free_action(gpointer act) {
326     Action *action = (Action*)act;
327     g_free(action->name);
328     if (action->param)
329         g_free(action->param);
330     g_free(action);
331 }
332
333 Action*
334 new_action(const gchar *name, const gchar *param) {
335     Action *action = g_new(Action, 1);
336
337     action->name = g_strdup(name);
338     if (param)
339         action->param = g_strdup(param);
340     else
341         action->param = NULL;
342
343     return action;
344 }
345
346 static bool
347 file_exists (const char * filename) {
348     FILE *file = fopen (filename, "r");
349     if (file) {
350         fclose (file);
351         return true;
352     }
353     return false;
354 }
355
356 void
357 set_insert_mode(WebKitWebView *page, const gchar *param) {
358     (void)page;
359     (void)param;
360
361     insert_mode = TRUE;
362     update_title();
363 }
364
365 static void
366 load_uri (WebKitWebView * web_view, const gchar *param) {
367     if (param) {
368         GString* newuri = g_string_new (param);
369         if (g_strrstr (param, "://") == NULL)
370             g_string_prepend (newuri, "http://"); 
371         webkit_web_view_load_uri (web_view, newuri->str);
372         g_string_free (newuri, TRUE);
373     }
374 }
375
376 static void
377 new_window_load_uri (const gchar * uri) {
378     GString* to_execute = g_string_new ("");
379     g_string_append_printf (to_execute, "%s --uri '%s'", executable_path, uri);
380     int i;
381     for (i = 0; entries[i].long_name != NULL; i++) {
382         if ((entries[i].arg == G_OPTION_ARG_STRING) && (strcmp(entries[i].long_name,"uri")!=0)) {
383             gchar** str = (gchar**)entries[i].arg_data;
384             if (*str!=NULL) {
385                 g_string_append_printf (to_execute, " --%s '%s'", entries[i].long_name, *str);
386             }
387         }
388     }
389     printf("\n%s\n", to_execute->str);
390     g_spawn_command_line_async (to_execute->str, NULL);
391     g_string_free (to_execute, TRUE);
392 }
393
394 static void
395 close_uzbl (WebKitWebView *page, const char *param) {
396     (void)page;
397     (void)param;
398     gtk_main_quit ();
399 }
400
401 // make sure to put '' around args, so that if there is whitespace we can still keep arguments together.
402 static gboolean
403 run_command(const char *command, const char *args) {
404    //command <uzbl conf> <uzbl pid> <uzbl win id> <uzbl fifo file> <uzbl socket file> [args]
405     GString* to_execute = g_string_new ("");
406     gboolean result;
407     g_string_printf (to_execute, "%s '%s' '%i' '%i' '%s' '%s'", command, config_file, (int) getpid() , (int) xwin, fifo_path, socket_path);
408     g_string_append_printf (to_execute, " '%s' '%s'", uri, "TODO title here");
409     if(args) {
410         g_string_append_printf (to_execute, " %s", args);
411     }
412     result = g_spawn_command_line_async (to_execute->str, NULL);
413     printf("Called %s.  Result: %s\n", to_execute->str, (result ? "TRUE" : "FALSE" ));
414     g_string_free (to_execute, TRUE);
415     return result;
416 }
417
418 static void
419 spawn(WebKitWebView *web_view, const char *param) {
420     (void)web_view;
421     run_command(param, NULL);
422 }
423
424 static void
425 parse_command(const char *cmd, const char *param) {
426     Command c;
427
428     if ((c = g_hash_table_lookup(commands, cmd)))
429         c(web_view, param);
430     else
431         fprintf (stderr, "command \"%s\" not understood. ignoring.\n", cmd);
432 }
433
434 static void
435 parse_line(char *line) {
436     gchar **parts;
437
438     g_strstrip(line);
439
440     parts = g_strsplit(line, " ", 2);
441
442     if (!parts)
443         return;
444
445     parse_command(parts[0], parts[1]);
446
447     g_strfreev(parts);
448 }
449
450 static void
451 control_fifo(GIOChannel *fd) {
452     gchar *ctl_line;
453     gsize term_pos;
454
455     if(!fd)
456        return;
457
458     g_io_channel_read_line(fd, &ctl_line, NULL, &term_pos, NULL);
459     ctl_line[term_pos] ='\0';
460
461     parse_line(ctl_line);
462
463     g_free(ctl_line);
464
465     return;
466 }
467
468 static void
469 create_fifo() {
470     GIOChannel *chan = NULL;
471
472     if (fifo_dir) {
473         sprintf (fifo_path, "%s/uzbl_fifo_%d", fifo_dir, (int) xwin);
474     } else {
475         sprintf (fifo_path, "/tmp/uzbl_fifo_%d", (int) xwin);
476     }
477     printf ("Control fifo opened in %s\n", fifo_path);
478     if (mkfifo (fifo_path, 0666) == -1) {
479         printf ("Possible error creating fifo\n");
480     }
481
482     if( (chan = g_io_channel_new_file((gchar *) fifo_path, "r+", NULL)) )
483         g_io_add_watch(chan, G_IO_IN|G_IO_HUP, (GIOFunc) control_fifo, chan);
484     return;
485 }
486
487 static void
488 *control_socket() {
489     if (socket_dir) {
490         sprintf (socket_path, "%s/uzbl_socket_%d", socket_dir, (int) xwin);
491     } else {
492         sprintf (socket_path, "/tmp/uzbl_socket_%d", (int) xwin);
493     }
494  
495     int sock, clientsock, len;
496     unsigned int t;
497     struct sockaddr_un local, remote;
498
499     sock = socket (AF_UNIX, SOCK_STREAM, 0);
500
501     local.sun_family = AF_UNIX;
502     strcpy (local.sun_path, socket_path);
503     unlink (local.sun_path);
504
505     len = strlen (local.sun_path) + sizeof (local.sun_family);
506     bind (sock, (struct sockaddr *) &local, len);
507
508     if (errno == -1) {
509         printf ("A problem occurred when opening a socket in %s\n", socket_path);
510     } else {
511         printf ("Control socket opened in %s\n", socket_path);
512     }
513
514     listen (sock, 5);
515  
516     char buffer[512];
517     char temp[128];
518     int done, n;
519     for(;;) {
520         memset (buffer, 0, sizeof (buffer));
521
522         t          = sizeof (remote);
523         clientsock = accept (sock, (struct sockaddr *) &remote, &t);
524         printf ("Connected to client\n");
525
526         done = 0;
527         do {
528             memset (temp, 0, sizeof (temp));
529             n = recv (clientsock, temp, 128, 0);
530             if (n == 0) {
531                 buffer[strlen (buffer)] = '\0';
532                 done = 1;
533             }
534
535             if (!done)
536                 strcat (buffer, temp);
537         } while (!done);
538
539         if (strcmp (buffer, "\n") < 0) {
540             buffer[strlen (buffer) - 1] = '\0';
541         } else {
542           buffer[strlen (buffer)] = '\0';
543         }
544
545         parse_line (buffer);
546         close (clientsock);
547     }
548
549     return NULL;
550
551  
552 static void
553 setup_threading () {
554     pthread_t control_thread;
555     pthread_create(&control_thread, NULL, control_socket, NULL);
556     pthread_detach(control_thread);
557 }
558
559 static void
560 update_title (void) {
561     GString* string_long = g_string_new ("");
562     GString* string_short = g_string_new ("");
563
564     g_string_append_printf(string_long, "%s ", keycmd->str);
565     if (!always_insert_mode)
566         g_string_append (string_long, (insert_mode ? "[I] " : "[C] "));
567     if (main_title) {
568         g_string_append (string_long, main_title);
569         g_string_append (string_short, main_title);
570     }
571     g_string_append (string_long, " - Uzbl browser");
572     g_string_append (string_short, " - Uzbl browser");
573     if (load_progress < 100)
574         g_string_append_printf (string_long, " (%d%%)", load_progress);
575
576     if (selected_url[0]!=0) {
577         g_string_append_printf (string_long, " -> (%s)", selected_url);
578     }
579
580     gchar* title_long = g_string_free (string_long, FALSE);
581     gchar* title_short = g_string_free (string_short, FALSE);
582
583     if (show_status) {
584         gtk_window_set_title (GTK_WINDOW(main_window), title_short);
585     gtk_label_set_text(GTK_LABEL(mainbar_label), title_long);
586     } else {
587         gtk_window_set_title (GTK_WINDOW(main_window), title_long);
588     }
589
590     g_free (title_long);
591     g_free (title_short);
592 }
593  
594 static gboolean
595 key_press_cb (WebKitWebView* page, GdkEventKey* event)
596 {
597     //TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.
598
599     (void) page;
600     Action *action;
601
602     if (event->type != GDK_KEY_PRESS || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down
603         || event->keyval == GDK_Up || event->keyval == GDK_Down || event->keyval == GDK_Left || event->keyval == GDK_Right)
604         return FALSE;
605
606     /* turn off insert mode (if always_insert_mode is not used) */
607     if (insert_mode && (event->keyval == GDK_Escape)) {
608         insert_mode = always_insert_mode;
609         update_title();
610         return TRUE;
611     }
612
613     if (insert_mode && event->state != modmask)
614         return FALSE;
615
616
617     if (event->keyval == GDK_Escape) {
618         g_string_truncate(keycmd, 0);
619
620         update_title();
621
622         return TRUE;
623     }
624
625     g_string_append(keycmd, event->string);
626
627     if ((action = g_hash_table_lookup(bindings, keycmd->str))) {
628         g_string_truncate(keycmd, 0);
629
630         parse_command(action->name, action->param);
631     }
632
633     update_title();
634
635     return TRUE;
636 }
637
638 static GtkWidget*
639 create_browser () {
640     GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
641     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_NEVER); //todo: some sort of display of position/total length. like what emacs does
642
643     web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
644     gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (web_view));
645
646     g_signal_connect (G_OBJECT (web_view), "title-changed", G_CALLBACK (title_change_cb), web_view);
647     g_signal_connect (G_OBJECT (web_view), "load-progress-changed", G_CALLBACK (progress_change_cb), web_view);
648     g_signal_connect (G_OBJECT (web_view), "load-committed", G_CALLBACK (load_commit_cb), web_view);
649     g_signal_connect (G_OBJECT (web_view), "load-committed", G_CALLBACK (log_history_cb), web_view);
650     g_signal_connect (G_OBJECT (web_view), "hovering-over-link", G_CALLBACK (link_hover_cb), web_view);
651     g_signal_connect (G_OBJECT (web_view), "key-press-event", G_CALLBACK (key_press_cb), web_view);
652     g_signal_connect (G_OBJECT (web_view), "new-window-policy-decision-requested", G_CALLBACK (new_window_cb), web_view); 
653     g_signal_connect (G_OBJECT (web_view), "download-requested", G_CALLBACK (download_cb), web_view); 
654     g_signal_connect (G_OBJECT (web_view), "create-web-view", G_CALLBACK (create_web_view_cb), web_view);  
655
656     return scrolled_window;
657 }
658
659 static GtkWidget*
660 create_mainbar () {
661     mainbar = gtk_hbox_new (FALSE, 0);
662     mainbar_label = gtk_label_new ("");  
663     gtk_misc_set_alignment (GTK_MISC(mainbar_label), 0, 0);
664     gtk_misc_set_padding (GTK_MISC(mainbar_label), 2, 2);
665     gtk_box_pack_start (GTK_BOX (mainbar), mainbar_label, TRUE, TRUE, 0);
666     return mainbar;
667 }
668
669 static
670 GtkWidget* create_window () {
671     GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
672     gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
673     gtk_widget_set_name (window, "Uzbl browser");
674     g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
675
676     return window;
677 }
678
679 static void
680 add_binding (const gchar *key, const gchar *act) {
681     char **parts = g_strsplit(act, " ", 2);
682     Action *action;
683
684     if (!parts)
685         return;
686
687     action = new_action(parts[0], parts[1]);
688     g_hash_table_insert(bindings, g_strdup(key), action);
689
690     g_strfreev(parts);
691 }
692
693 static void
694 settings_init () {
695     GKeyFile* config;
696     gboolean res  = FALSE;
697     char *saveptr;
698     gchar** keys = NULL;
699
700     if (!config_file) {
701         const char* XDG_CONFIG_HOME = getenv ("XDG_CONFIG_HOME");
702         if (! XDG_CONFIG_HOME || ! strcmp (XDG_CONFIG_HOME, "")) {
703           XDG_CONFIG_HOME = (char*)XDG_CONFIG_HOME_default;
704         }
705         printf("XDG_CONFIG_HOME: %s\n", XDG_CONFIG_HOME);
706     
707         strcpy (config_file_path, XDG_CONFIG_HOME);
708         strcat (config_file_path, "/uzbl/config");
709         if (file_exists (config_file_path)) {
710           printf ("Config file %s found.\n", config_file_path);
711           config_file = &config_file_path[0];
712         } else {
713             // Now we check $XDG_CONFIG_DIRS
714             char *XDG_CONFIG_DIRS = getenv ("XDG_CONFIG_DIRS");
715             if (! XDG_CONFIG_DIRS || ! strcmp (XDG_CONFIG_DIRS, ""))
716                 XDG_CONFIG_DIRS = XDG_CONFIG_DIRS_default;
717
718             printf("XDG_CONFIG_DIRS: %s\n", XDG_CONFIG_DIRS);
719
720             char buffer[512];
721             strcpy (buffer, XDG_CONFIG_DIRS);
722             const gchar* dir = (char *) strtok_r (buffer, ":", &saveptr);
723             while (dir && ! file_exists (config_file_path)) {
724                 strcpy (config_file_path, dir);
725                 strcat (config_file_path, "/uzbl/config_file_pathig");
726                 if (file_exists (config_file_path)) {
727                     printf ("Config file %s found.\n", config_file_path);
728                     config_file = &config_file_path[0];
729                 }
730                 dir = (char * ) strtok_r (NULL, ":", &saveptr);
731             }
732         }
733     }
734
735     if (config_file) {
736         config = g_key_file_new ();
737         res = g_key_file_load_from_file (config, config_file, G_KEY_FILE_NONE, NULL);
738           if(res) {
739             printf ("Config %s loaded\n", config_file);
740           } else {
741             fprintf (stderr, "Config %s loading failed\n", config_file);
742         }
743     } else {
744         printf ("No configuration.\n");
745     }
746
747     if (res) {
748         history_handler    = g_key_file_get_value   (config, "behavior", "history_handler",    NULL);
749         download_handler   = g_key_file_get_value   (config, "behavior", "download_handler",   NULL);
750         always_insert_mode = g_key_file_get_boolean (config, "behavior", "always_insert_mode", NULL);
751         show_status        = g_key_file_get_boolean (config, "behavior", "show_status",        NULL);
752         modkey             = g_key_file_get_value   (config, "behavior", "modkey",             NULL);
753         status_top         = g_key_file_get_boolean (config, "behavior", "status_top",         NULL);
754         if (! fifo_dir)
755             fifo_dir        = g_key_file_get_value  (config, "behavior", "fifo_dir",           NULL);
756         if (! socket_dir)
757             socket_dir     = g_key_file_get_value   (config, "behavior", "socket_dir",         NULL);
758         keys               = g_key_file_get_keys    (config, "bindings", NULL,                 NULL);
759     }
760     
761     printf ("History handler: %s\n",    (history_handler    ? history_handler  : "disabled"));
762     printf ("Download manager: %s\n",   (download_handler   ? download_handler : "disabled"));
763     printf ("Fifo directory: %s\n",     (fifo_dir           ? fifo_dir         : "/tmp"));
764     printf ("Socket directory: %s\n",   (socket_dir         ? socket_dir       : "/tmp"));
765     printf ("Always insert mode: %s\n", (always_insert_mode ? "TRUE"           : "FALSE"));
766     printf ("Show status: %s\n",        (show_status        ? "TRUE"           : "FALSE"));
767     printf ("Status top: %s\n",         (status_top         ? "TRUE"           : "FALSE"));
768     printf ("Modkey: %s\n",             (modkey             ? modkey           : "disabled"));
769
770     if (! modkey)
771         modkey = "";
772
773     //POSSIBLE MODKEY VALUES (COMBINATIONS CAN BE USED)
774     gchar* modkeyup = g_utf8_strup (modkey, -1);
775     if (g_strrstr (modkeyup,"SHIFT") != NULL)    modmask |= GDK_SHIFT_MASK;    //the Shift key.
776     if (g_strrstr (modkeyup,"LOCK") != NULL)     modmask |= GDK_LOCK_MASK;     //a Lock key (depending on the modifier mapping of the X server this may either be CapsLock or ShiftLock).
777     if (g_strrstr (modkeyup,"CONTROL") != NULL)  modmask |= GDK_CONTROL_MASK;  //the Control key.
778     if (g_strrstr (modkeyup,"MOD1") != NULL)     modmask |= GDK_MOD1_MASK;     //the fourth modifier key (it depends on the modifier mapping of the X server which key is interpreted as this modifier, but normally it is the Alt key).
779     if (g_strrstr (modkeyup,"MOD2") != NULL)     modmask |= GDK_MOD2_MASK;     //the fifth modifier key (it depends on the modifier mapping of the X server which key is interpreted as this modifier).
780     if (g_strrstr (modkeyup,"MOD3") != NULL)     modmask |= GDK_MOD3_MASK;     //the sixth modifier key (it depends on the modifier mapping of the X server which key is interpreted as this modifier).
781     if (g_strrstr (modkeyup,"MOD4") != NULL)     modmask |= GDK_MOD4_MASK;     //the seventh modifier key (it depends on the modifier mapping of the X server which key is interpreted as this modifier).
782     if (g_strrstr (modkeyup,"MOD5") != NULL)     modmask |= GDK_MOD5_MASK;     //the eighth modifier key (it depends on the modifier mapping of the X server which key is interpreted as this modifier).
783     if (g_strrstr (modkeyup,"BUTTON1") != NULL)  modmask |= GDK_BUTTON1_MASK;  //the first mouse button.
784     if (g_strrstr (modkeyup,"BUTTON2") != NULL)  modmask |= GDK_BUTTON2_MASK;  //the second mouse button.
785     if (g_strrstr (modkeyup,"BUTTON3") != NULL)  modmask |= GDK_BUTTON3_MASK;  //the third mouse button.
786     if (g_strrstr (modkeyup,"BUTTON4") != NULL)  modmask |= GDK_BUTTON4_MASK;  //the fourth mouse button.
787     if (g_strrstr (modkeyup,"BUTTON5") != NULL)  modmask |= GDK_BUTTON5_MASK;  //the fifth mouse button.
788     if (g_strrstr (modkeyup,"SUPER") != NULL)    modmask |= GDK_SUPER_MASK;    //the Super modifier. Since 2.10
789     if (g_strrstr (modkeyup,"HYPER") != NULL)    modmask |= GDK_HYPER_MASK;    //the Hyper modifier. Since 2.10
790     if (g_strrstr (modkeyup,"META") != NULL)     modmask |= GDK_META_MASK;     //the Meta modifier. Since 2.10  */
791     free (modkeyup);
792
793     if (keys) {
794         int i;
795         for (i = 0; keys[i]; i++) {
796             gchar *value = g_key_file_get_string (config, "bindings", keys[i], NULL);
797             
798             add_binding(g_strstrip(keys[i]), value);
799             g_free(value);
800         }
801
802         g_strfreev(keys);
803     }
804
805         /* networking options */
806         proxy_url = g_key_file_get_value(config, "network", "proxy_server", NULL);
807         http_debug = g_key_file_get_integer(config, "network", "http_debug", NULL);
808         useragent = g_key_file_get_value(config, "network", "user-agent", NULL);
809         max_conns = g_key_file_get_integer(config, "network", "max_conns", NULL);
810         max_conns_host = g_key_file_get_integer(config, "network", "max_conns_per_host", NULL);
811
812         if(proxy_url){
813                 g_object_set(G_OBJECT(soup_session), SOUP_SESSION_PROXY_URI, soup_uri_new(proxy_url), NULL);
814                 printf("Proxy configured: %s\n", proxy_url ? proxy_url : "none");
815         }
816         
817         if(!(http_debug <= 3)){
818                 http_debug = 0;
819                 fprintf(stderr, "Wrong http_debug level, ignoring.\n");
820         } else {
821                 soup_logger = soup_logger_new(http_debug, -1);
822                 soup_session_add_feature(soup_session, SOUP_SESSION_FEATURE(soup_logger));
823                 printf("HTTP logging level: %d\n", http_debug);
824         }
825         
826         if(useragent){
827                 g_object_set(G_OBJECT(soup_session), SOUP_SESSION_USER_AGENT, useragent, NULL);
828                 printf("User-agent: %s\n", useragent);
829         } else {
830                 printf("User-agent: webkit default\n");
831         }
832
833         if(max_conns >= 1){
834                 g_object_set(G_OBJECT(soup_session), SOUP_SESSION_MAX_CONNS, max_conns, NULL);
835                 printf("Maximum connections set to: %d\n", max_conns);
836         }
837         if(max_conns_host >= 1){
838                 g_object_set(G_OBJECT(soup_session), SOUP_SESSION_MAX_CONNS_PER_HOST, max_conns_host, NULL);
839                 printf("Maximum connections per host set to: %d\n", max_conns_host);
840         }
841                 
842 }
843
844 int
845 main (int argc, char* argv[]) {
846     gtk_init (&argc, &argv);
847     if (!g_thread_supported ())
848         g_thread_init (NULL);
849
850     printf("Uzbl start location: %s\n", argv[0]);
851     strcpy(executable_path,argv[0]);
852
853     strcat ((char *) XDG_CONFIG_HOME_default, getenv ("HOME"));
854     strcat ((char *) XDG_CONFIG_HOME_default, "/.config");
855
856     GError *error = NULL;
857     GOptionContext* context = g_option_context_new ("- some stuff here maybe someday");
858     g_option_context_add_main_entries (context, entries, NULL);
859     g_option_context_add_group (context, gtk_get_option_group (TRUE));
860     g_option_context_parse (context, &argc, &argv, &error);
861     /* initialize hash table */
862     bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, free_action);
863         
864         soup_session = webkit_get_default_session();
865     keycmd = g_string_new("");
866
867     settings_init ();
868     commands_hash ();
869
870     if (always_insert_mode)
871         insert_mode = TRUE;
872
873     GtkWidget* vbox = gtk_vbox_new (FALSE, 0);
874     if (status_top)
875         gtk_box_pack_start (GTK_BOX (vbox), create_mainbar (), FALSE, TRUE, 0);
876     gtk_box_pack_start (GTK_BOX (vbox), create_browser (), TRUE, TRUE, 0);
877     if (!status_top)
878         gtk_box_pack_start (GTK_BOX (vbox), create_mainbar (), FALSE, TRUE, 0);
879
880     main_window = create_window ();
881     gtk_container_add (GTK_CONTAINER (main_window), vbox);
882
883     load_uri (web_view, uri);
884
885     gtk_widget_grab_focus (GTK_WIDGET (web_view));
886     gtk_widget_show_all (main_window);
887     xwin = GDK_WINDOW_XID (GTK_WIDGET (main_window)->window);
888     printf("window_id %i\n",(int) xwin);
889     printf("pid %i\n", getpid ());
890
891     scbar_v = (GtkScrollbar*) gtk_vscrollbar_new (NULL);
892     bar_v = gtk_range_get_adjustment((GtkRange*) scbar_v);
893     scbar_h = (GtkScrollbar*) gtk_hscrollbar_new (NULL);
894     bar_h = gtk_range_get_adjustment((GtkRange*) scbar_h);
895     gtk_widget_set_scroll_adjustments ((GtkWidget*) web_view, bar_h, bar_v);
896
897     if (!show_status)
898         gtk_widget_hide(mainbar);
899
900     setup_threading ();
901     create_fifo ();
902
903     gtk_main ();
904
905     g_string_free(keycmd, TRUE);
906
907     unlink (socket_path);
908     unlink (fifo_path);
909
910     g_hash_table_destroy(bindings);
911     g_hash_table_destroy(commands);
912     return 0;
913 }
914
915 /* vi: set et ts=4: */