ntl-web-browser.c 13.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
#include <WWWCore.h>
#include <WWWStream.h>
#include <WWWTrans.h>
#include <WWWHTTP.h>
#include <WWWMIME.h>
#include <WWWFTP.h>
#include <WWWFile.h>
#include <WWWGophe.h>
#include <WWWZip.h>

/* clean up the cpp namespace -- libwww is particularly dirty */
#undef PACKAGE
#undef VERSION
#undef _

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "glibwww.h"
21 22 23
#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#endif
24 25 26
#include <gnome.h>
#include <libnautilus/libnautilus.h>
#include <gtkhtml/gtkhtml.h>
27
#include <libgnorba/gnorba.h>
28
#include <libgnomevfs/gnome-vfs.h>
29 30

typedef struct {
31
  NautilusViewFrame *view_frame;
32 33
  GtkWidget *htmlw;
  char *base_url, *base_target_url;
34 35 36 37 38

  int prevsel;

  HTMethod method;
  char *post_data;
39 40
} BrowserInfo;

41 42 43 44 45 46 47 48 49 50 51
typedef struct {
  GtkHTMLStreamHandle sh;
  BrowserInfo *bi;
  char *url;
  HTStream *stream;
} VFSHandle;

extern GtkHTMLStreamHandle gtk_html_stream_ref(GtkHTMLStreamHandle handle);
extern void gtk_html_stream_unref(GtkHTMLStreamHandle handle);
static void do_vfs_load(VFSHandle *handle);

52
static char *
53 54
canonicalize_url (const char *in_url, const char *base_url)
{
55
  char *ctmp, *ctmp2, *retval, *removebegin, *removeend, *curpos;
56

57
  g_return_val_if_fail(in_url, NULL);
58

59 60 61 62 63 64 65 66 67 68
  ctmp = strstr(in_url, "://");
  if(ctmp)
    {
      retval = g_strdup(in_url);
      goto out;
    }
  else if(*in_url == '/')
    {
      ctmp = base_url?strstr(base_url, "://"):NULL;
      if(!ctmp)
69
	{
70 71
	  retval = g_strconcat("file://", in_url, NULL);
	  goto out;
72 73
	}

74
      ctmp2 = strchr(ctmp + 3, '/');
75

76 77 78
      retval = g_strconcat(base_url, in_url, NULL);
      goto out;
    }
79

80
  /* XXX TODO - We should really do processing of .. and . in URLs */
81

82 83 84 85
  ctmp = base_url?strstr(base_url, "://"):NULL;
  if(!ctmp)
    {
      char *cwd;
86

87 88 89
      cwd = g_get_current_dir();
      ctmp = g_strconcat("file://", cwd, "/", in_url, NULL);
      g_free(cwd);
90

91 92 93
      retval = ctmp;
      goto out;
    }
94

95
  retval = g_strconcat(base_url, "/", in_url, NULL);
96 97

 out:
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
  /* Now fix up the /. and /.. pieces */

  ctmp = strstr(retval, "://");
  g_assert(ctmp);
  ctmp += 3;
  ctmp = strchr(ctmp, '/');
  if(!ctmp) {
    ctmp = retval;
    retval = g_strconcat(retval, "/", NULL);
    g_free(ctmp);
    return retval;
  }

  removebegin = removeend = NULL;
  do {
    if(removebegin && removeend)
      {
	memmove(removebegin, removeend, strlen(removeend) + 1);
116
	removebegin = removeend = NULL;
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
      }
    curpos = ctmp;

  redo:
    ctmp2 = strstr(curpos, "/.");
    if(!ctmp2)
      break;

    if(*(ctmp2 + 2) == '.') /* We have to skip over stuff like /...blahblah or /.foo */
      {
	if(*(ctmp2 + 3) != '/'
	   && *(ctmp2 + 3) != '\0')
	  {
	    curpos = ctmp2 + 3;
	    goto redo;
	  }
      }
    else if(*(ctmp2 + 2) != '/' && *(ctmp2 + 2) != '\0')
      {
	curpos = ctmp2 + 2;
	goto redo;
      }

    switch(*(ctmp2+2))
      {
      case '/':
      case '\0':
	removebegin = ctmp2;
	removeend = ctmp2 + 2;
	break;
      case '.':
	removeend = ctmp2 + 3;
	ctmp2--;
	while((ctmp2 >= ctmp) && *ctmp2 != '/')
	  ctmp2--;
	if(*ctmp2 == '/')
	  removebegin = ctmp2;
	break;
      }

  } while(removebegin);

  return retval;
160 161 162 163 164
}

static void
browser_url_load_done(GtkWidget *htmlw, BrowserInfo *bi)
{
165
  Nautilus_ProgressRequestInfo pri;
166 167 168

  gtk_html_calc_scrollbars(GTK_HTML(bi->htmlw));

169 170 171 172
  memset(&pri, 0, sizeof(pri));

  pri.type = Nautilus_PROGRESS_DONE_OK;
  pri.amount = 100.0;
173
  nautilus_view_frame_request_progress_change(bi->view_frame, &pri);
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
}

struct _HTStream {
  const HTStreamClass *	isa;
  BrowserInfo *bi;
  GtkHTMLStreamHandle handle;
};

static int netin_stream_write (HTStream * me, const char * s, int l)
{
  gtk_html_write(GTK_HTML(me->bi->htmlw), me->handle, s, l);

  return HT_OK;
}

static int netin_stream_put_character (HTStream * me, char c)
{
  return netin_stream_write(me, &c, 1);
}

static int netin_stream_put_string (HTStream * me, const char * s)
{
  return netin_stream_write(me, s, strlen(s));
}

static int netin_stream_flush (HTStream * me)
{
  return HT_OK;
}

static int netin_stream_free (HTStream * me)
{
206
  g_message("netin_stream_free");
207 208
  if(me->handle)
    gtk_html_end(GTK_HTML(me->bi->htmlw), me->handle, GTK_HTML_STREAM_OK);
209 210 211 212 213 214 215
  g_free(me);

  return HT_OK;
}

static int netin_stream_abort (HTStream * me, HTList * e)
{
216
  g_message("netin_stream_abort");
217 218
  if(me->handle)
    gtk_html_end(GTK_HTML(me->bi->htmlw), me->handle, GTK_HTML_STREAM_ERROR);
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
  g_free(me);

  return HT_OK;
}

static const HTStreamClass netin_stream_class =
{		
    "netin_stream",
    netin_stream_flush,
    netin_stream_free,
    netin_stream_abort,
    netin_stream_put_character,
    netin_stream_put_string,
    netin_stream_write
}; 

static HTStream *
netin_stream_new (BrowserInfo *bi, GtkHTMLStreamHandle handle)
{
  HTStream *retval;

  retval = g_new0(HTStream, 1);

  retval->isa = &netin_stream_class;
  retval->bi = bi;
244
  retval->handle = handle;
245 246 247 248

  return retval;
}

249 250 251 252 253 254 255 256 257
static gboolean
do_request_delete(gpointer req)
{
  HTRequest_delete(req);

  return FALSE;
}

static int
258 259 260
request_terminator (HTRequest * request, HTResponse * response, void * param, int status)
{
  gpointer d;
261
  VFSHandle *vfsh;
262

263 264
  d = HTRequest_context(request);
  g_return_val_if_fail(d, HT_OK);
265 266 267 268 269 270 271 272 273 274 275 276 277 278
  vfsh = d;

  if (status < 0)
    {
      g_print("Load couldn't be completed successfully (%p)\n", request);
      vfsh->stream->handle = NULL;
      vfsh->stream = NULL;
      do_vfs_load(vfsh);
    }
  else
    {
      g_free(vfsh->url);
      g_free(vfsh);
    }
279 280

  HTRequest_setContext(request, NULL);
281 282 283 284 285
  g_idle_add(do_request_delete, request);

  return HT_OK;
}

286 287 288
static int
browser_do_post(HTRequest *request, HTStream *stream)
{
289
  VFSHandle *vfsh = HTRequest_context(request);
290 291
  int status;

292
  g_assert(vfsh);
293

294
  status = (*stream->isa->put_block)(stream, vfsh->bi->post_data, strlen(vfsh->bi->post_data));
295 296 297 298 299 300 301

  g_message("browser_do_post got status %d", status);

  switch(status)
    {
    case HT_LOADED:
    case HT_OK:
302
      g_free(vfsh->bi->post_data); vfsh->bi->post_data = NULL;
303 304 305 306 307 308
      (*stream->isa->flush)(stream);
    default:
      return status;
      break;
    }
}
309

310 311 312 313 314 315 316 317 318 319
static char vfs_read_buf[4096];

static void
browser_vfs_read_callback(GnomeVFSAsyncHandle *h, GnomeVFSResult res, gpointer buffer,
			  GnomeVFSFileSize bytes_requested,
			  GnomeVFSFileSize bytes_read,
			  gpointer data)
{
  VFSHandle *vfsh = data;

320
  g_message("browser_vfs_read_callback: %ld/%ld bytes", bytes_read, bytes_requested);
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
  gtk_html_write(GTK_HTML(vfsh->bi->htmlw), vfsh->sh, buffer, bytes_read);

  if(res != GNOME_VFS_OK)
    {
      gtk_html_end(GTK_HTML(vfsh->bi->htmlw), vfsh->sh, GTK_HTML_STREAM_OK);
      gnome_vfs_async_close(h, (GnomeVFSAsyncCloseCallback)gtk_true, NULL);
      g_free(vfsh);
    }

  gnome_vfs_async_read(h, vfs_read_buf, sizeof(vfs_read_buf), browser_vfs_read_callback, data);
}

static void
browser_vfs_callback(GnomeVFSAsyncHandle *h, GnomeVFSResult res, gpointer data)
{
  VFSHandle *vfsh = data;

338 339
  g_message("browser_vfs_callback");

340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
  if(res != GNOME_VFS_OK)
    {
      gtk_html_end(GTK_HTML(vfsh->bi->htmlw), vfsh->sh, GTK_HTML_STREAM_ERROR);
      g_free(vfsh);
    }
  else
    gnome_vfs_async_read(h, vfs_read_buf, sizeof(vfs_read_buf), browser_vfs_read_callback, vfsh);
}

static void
do_vfs_load(VFSHandle *vfsh)
{
  GnomeVFSResult res;
  GnomeVFSAsyncHandle *ah;

  g_warning("Falling back to gnome-vfs for %s", vfsh->url);

  res = gnome_vfs_async_open(&ah, vfsh->url, GNOME_VFS_OPEN_READ, browser_vfs_callback, vfsh);
      
  return;
}

362 363 364 365 366 367
static void
browser_url_requested(GtkWidget *htmlw, const char *url, GtkHTMLStreamHandle handle, BrowserInfo *bi)
{
  char *real_url;
  HTRequest *request;
  HTStream *writer;
368 369
  HTAnchor *anchor;
  VFSHandle *vfsh;
370 371 372

  real_url = canonicalize_url(url, bi->base_url);

373 374 375 376 377 378 379 380 381 382 383 384
  vfsh = g_new0(VFSHandle, 1);
  vfsh->sh = handle;
  vfsh->bi = bi;
  vfsh->url = real_url;

  anchor = HTAnchor_findAddress(real_url);
  if(!anchor)
    {
      do_vfs_load(vfsh);
      return;
    }

385
  request = HTRequest_new();
386
  HTRequest_setContext(request, vfsh);
387
  writer = netin_stream_new(bi, handle);
388
  vfsh->stream = writer;
389 390
  HTRequest_setOutputFormat(request, WWW_SOURCE);
  HTRequest_setOutputStream(request, writer);
391 392
  if(bi->method == METHOD_POST)
    HTRequest_setPostCallback(request, browser_do_post);
393
  HTRequest_setAnchor(request, anchor);
394 395
  HTRequest_setMethod(request, bi->method);
  bi->method = METHOD_GET;
396 397 398

  if(HTLoad(request, NO) == NO)
    {
399
      g_warning("Load failed");
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
      HTRequest_delete(request);
    }
}

static void
browser_set_base(GtkWidget *htmlw, const char *base_url, BrowserInfo *bi)
{
  if(bi->base_url)
    g_free(bi->base_url);
  bi->base_url = g_strdup(base_url);
}

static void
browser_set_base_target(GtkWidget *htmlw, const char *base_target_url, BrowserInfo *bi)
{
  if(bi->base_target_url)
    g_free(bi->base_target_url);
  bi->base_target_url = g_strdup(base_target_url);
}

static void
browser_goto_url_real(GtkWidget *htmlw, const char *url, BrowserInfo *bi)
{
423 424 425 426 427
  Nautilus_ProgressRequestInfo pri;

  pri.type = Nautilus_PROGRESS_UNDERWAY;
  pri.amount = 0.0;

428
  HTNet_killAll();
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
  g_free(bi->base_url);
  g_free(bi->base_target_url);

  if(url[strlen(url) - 1] == '/')
    {
      bi->base_url = g_strdup(url);
      bi->base_target_url = g_strdup(url);
    }
  else
    {
      bi->base_url = g_dirname(url);
      bi->base_target_url = g_dirname(url);
    }

  gtk_html_begin(GTK_HTML(bi->htmlw), url);
444
  nautilus_view_frame_request_progress_change(bi->view_frame, &pri);
445 446 447 448 449 450 451 452 453 454 455 456 457 458
}

static void
browser_goto_url(GtkWidget *htmlw, const char *url, BrowserInfo *bi)
{
  Nautilus_NavigationRequestInfo nri;
  char *real_url;

  real_url = canonicalize_url(url, bi->base_target_url);

  g_return_if_fail(real_url);

  memset(&nri, 0, sizeof(nri));
  nri.requested_uri = real_url;
459
  nautilus_view_frame_request_location_change(bi->view_frame, &nri);
460 461 462 463
  browser_goto_url_real(htmlw, real_url, bi);
  g_free(real_url);
}

464 465 466 467
static void
browser_select_url(GtkWidget *htmlw, const char *url, BrowserInfo *bi)
{
  Nautilus_SelectionRequestInfo si;
468
  Nautilus_StatusRequestInfo sri;
469 470 471
  char *real_url = NULL;

  memset(&si, 0, sizeof(si));
472
  memset(&sri, 0, sizeof(sri));
473 474 475
  if(url && !bi->prevsel)
    {
      si.selected_uris._length = 1;
476
      sri.status_string = real_url = canonicalize_url(url, bi->base_target_url);
477 478 479 480 481 482 483
      si.selected_uris._buffer = &real_url;
    }
  else if(!url && bi->prevsel)
    {
      si.selected_uris._length = 0;
    }

484
  nautilus_view_frame_request_selection_change(bi->view_frame, &si);
485 486
  if(sri.status_string)
    nautilus_view_frame_request_status_change(bi->view_frame, &sri);
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520
  g_free(real_url);
  bi->prevsel = url?1:0;
}

static void
browser_submit(GtkWidget *htmlw, const char *method, const char *url, const char *encoding, BrowserInfo *bi)
{
  g_free(bi->post_data); bi->post_data = NULL;

  if(!strcasecmp(method, "POST"))
    {
      char **pieces = g_strsplit(encoding, "&", -1);

      if(pieces)
	{
	  char *ctmp;
	  ctmp = g_strjoinv("\r\n", pieces);
	  bi->post_data = g_strconcat(ctmp, "\r\n", NULL);
	  g_free(ctmp);
	  g_strfreev(pieces);
	  bi->method = METHOD_POST;
	}

      browser_goto_url(htmlw, url, bi);
    }
  else
    {
      char tmp_url[4096];

      g_snprintf(tmp_url, sizeof(tmp_url), "%s?%s", url, encoding);
      browser_goto_url(htmlw, tmp_url, bi);
    }
}

521
static void
522
browser_notify_location_change(NautilusViewFrame *view_frame, Nautilus_NavigationInfo *ni, BrowserInfo *bi)
523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539
{
  if(ni->self_originated)
    return;

  browser_goto_url_real(NULL, ni->requested_uri, bi);
}

static int object_count = 0;

static void
browser_do_destroy(GtkObject *obj)
{
  object_count--;
  if(object_count <= 0)
    gtk_main_quit();
}

540 541
static BonoboObject *
make_obj(BonoboGenericFactory *Factory, const char *goad_id, void *closure)
542 543 544 545 546 547 548 549
{
  BrowserInfo *bi;
  GtkWidget *wtmp;

  if(strcmp(goad_id, "ntl_web_browser"))
    return NULL;

  bi = g_new0(BrowserInfo, 1);
550 551
  bi->view_frame = NAUTILUS_VIEW_FRAME(gtk_widget_new(nautilus_content_view_frame_get_type(), NULL));
  gtk_signal_connect(GTK_OBJECT(bi->view_frame), "notify_location_change", browser_notify_location_change,
552
		     bi);
553
  gtk_signal_connect(GTK_OBJECT(bi->view_frame), "destroy", browser_do_destroy, NULL);
554 555 556 557 558 559 560 561 562
  object_count++;

  bi->htmlw = gtk_html_new();
  gtk_signal_connect(GTK_OBJECT(bi->htmlw), "link_clicked", browser_goto_url, bi);
  gtk_signal_connect(GTK_OBJECT(bi->htmlw), "set_base", browser_set_base, bi);
  gtk_signal_connect(GTK_OBJECT(bi->htmlw), "load_done", browser_url_load_done, bi);
  gtk_signal_connect(GTK_OBJECT(bi->htmlw), "set_base", browser_set_base, bi);
  gtk_signal_connect(GTK_OBJECT(bi->htmlw), "set_base_target", browser_set_base_target, bi);
  gtk_signal_connect(GTK_OBJECT(bi->htmlw), "url_requested", browser_url_requested, bi);
563 564
  gtk_signal_connect(GTK_OBJECT(bi->htmlw), "on_url", browser_select_url, bi);
  gtk_signal_connect(GTK_OBJECT(bi->htmlw), "submit", browser_submit, bi);
565 566 567

  wtmp = gtk_scrolled_window_new(NULL, NULL);
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(wtmp), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
568
  gtk_container_add(GTK_CONTAINER(bi->view_frame), wtmp);
569 570 571 572

  gtk_container_add(GTK_CONTAINER(wtmp), bi->htmlw);
  gtk_widget_show(bi->htmlw);
  gtk_widget_show(wtmp);
573
  gtk_widget_show(GTK_WIDGET(bi->view_frame));
574

575
  return nautilus_view_frame_get_bonobo_object(bi->view_frame);
576 577 578 579
}

int main(int argc, char *argv[])
{
580
  BonoboGenericFactory *factory;
581 582 583 584 585 586
  CORBA_ORB orb;
  CORBA_Environment ev;

  CORBA_exception_init(&ev);
  orb = gnome_CORBA_init_with_popt_table("ntl-web-browser", VERSION, &argc, argv, NULL, 0, NULL,
					 GNORBA_INIT_SERVER_FUNC, &ev);
587
  gnome_vfs_init();
588 589
  gdk_rgb_init();
  glibwww_init("ntl-web-browser", VERSION);
590
  HTNet_addAfter(request_terminator, NULL, NULL, HT_ALL, HT_FILTER_LAST);
591 592
  bonobo_init(orb, CORBA_OBJECT_NIL, CORBA_OBJECT_NIL);

593
  factory = bonobo_generic_factory_new_multi("ntl_web_browser_factory", make_obj, NULL);
594 595 596 597 598 599 600

  do {
    bonobo_main();
  } while(object_count > 0);

  return 0;
}