Thumbnail file hits. Based on a patch from D Bera
[beagle.git] / beagled / WebServices / WebBackEnd.cs
blob38ec4917dd05dbb3cd636f54ab248c493e538e60
1 //
2 // WebBackEnd.cs
3 //
4 // Copyright (C) 2005 Novell, Inc.
5 //
6 // Authors:
7 // Vijay K. Nanjundaswamy (knvijay@novell.com)
8 //
11 // Permission is hereby granted, free of charge, to any person obtaining a
12 // copy of this software and associated documentation files (the "Software"),
13 // to deal in the Software without restriction, including without limitation
14 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 // and/or sell copies of the Software, and to permit persons to whom the
16 // Software is furnished to do so, subject to the following conditions:
18 // The above copyright notice and this permission notice shall be included in
19 // all copies or substantial portions of the Software.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 // DEALINGS IN THE SOFTWARE.
29 using System;
30 using System.Text;
31 using System.Collections;
32 using System.Diagnostics;
33 using System.Threading;
34 using System.Reflection;
35 using System.IO;
37 using System.Runtime.Remoting;
38 using System.Runtime.Remoting.Channels;
39 using System.Runtime.Remoting.Channels.Tcp;
41 using Beagle;
42 using Beagle.Util;
43 using BT = Beagle.Tile;
44 using Beagle.Daemon;
46 namespace Beagle.WebService {
48 [Serializable()]
49 public struct webArgs
51 public string sessId;
52 public string searchString;
53 public string searchSource;
54 public bool isLocalReq;
55 public bool globalSearch;
58 public class WebBackEnd: MarshalByRefObject{
60 static WebBackEnd instance = null;
61 static Logger log = Logger.Get ("WebBackEnd");
63 private Hashtable result;
64 private Hashtable sessionResp;
66 public WebBackEnd() {
67 result = Hashtable.Synchronized(new Hashtable());
68 sessionResp = Hashtable.Synchronized(new Hashtable());
71 ~WebBackEnd() {
72 result.Clear();
73 sessionResp.Clear();
76 public bool allowGlobalAccess {
77 get { return WebServiceBackEnd.web_global; }
80 public string HostName {
81 get { return WebServiceBackEnd.hostname; }
84 public static void init()
87 if (instance == null) {
88 instance = new WebBackEnd();
90 //Register TCP Channel Listener
91 ChannelServices.RegisterChannel(new TcpChannel(8347));
93 WellKnownServiceTypeEntry WKSTE =
94 new WellKnownServiceTypeEntry(typeof(WebBackEnd),
95 "WebBackEnd.rem", WellKnownObjectMode.Singleton);
96 RemotingConfiguration.ApplicationName="beagled";
97 RemotingConfiguration.RegisterWellKnownServiceType(WKSTE);
101 void OnHitsAdded (QueryResult qres, ICollection hits)
103 if (result.Contains(qres)) {
105 Resp resp = ((Resp) result[qres]);
106 BT.SimpleRootTile root = resp.resultPair.rootTile;
107 ArrayList hitsCopy = resp.resultPair.hitsCopy;
109 lock (root) {
110 if (resp.isLocalReq) {
111 root.Add(hits);
112 lock (hitsCopy.SyncRoot)
113 hitsCopy.AddRange(hits);
115 else {
116 foreach (Hit h in hits)
117 if (h.Uri.ToString().StartsWith(NetworkedBeagle.BeagleNetPrefix) ||
118 WebServiceBackEnd.AccessFilter.FilterHit(h)) {
119 root.Add(h);
120 lock (hitsCopy.SyncRoot)
121 hitsCopy.Add(h);
128 void removeUris(ArrayList res, ICollection uris)
130 foreach(Uri u in uris)
131 foreach(Hit h in res)
132 if (h.Uri.Equals (u) && h.Uri.Fragment == u.Fragment) {
133 lock (res.SyncRoot) {
134 res.Remove(h);
136 break;
140 void OnHitsSubtracted (QueryResult qres, ICollection uris)
142 if (result.Contains(qres)) {
143 BT.SimpleRootTile root = ((Resp) result[qres]).resultPair.rootTile;
144 lock (root) {
145 root.Subtract (uris);
146 removeUris(((Resp) result[qres]).resultPair.hitsCopy, uris);
151 void OnFinished (QueryResult qres)
153 if (result.Contains(qres))
154 log.Info("WebBackEnd:OnFinished() - Got {0} results from beagled QueryDriver", ((Resp) result[qres]).resultPair.rootTile.HitCollection.NumResults);
156 DetachQueryResult(qres);
159 void OnCancelled (QueryResult qres)
161 DetachQueryResult(qres);
164 private void AttachQueryResult (QueryResult qres, Resp resp)
166 if (qres != null) {
168 qres.HitsAddedEvent += OnHitsAdded;
169 qres.HitsSubtractedEvent += OnHitsSubtracted;
170 qres.FinishedEvent += OnFinished;
171 qres.CancelledEvent += OnCancelled;
173 result.Add(qres, resp);
177 private void DetachQueryResult (QueryResult qres)
179 if (qres != null) {
181 if (result.Contains(qres))
183 Resp resp = ((Resp) result[qres]);
184 ArrayList hitsCopy = resp.resultPair.hitsCopy;
185 if (hitsCopy != null)
186 hitsCopy.Sort();
188 resp.bufferContext.maxDisplayed = 0;
190 result.Remove(qres);
193 qres.HitsAddedEvent -= OnHitsAdded;
194 qres.HitsSubtractedEvent -= OnHitsSubtracted;
195 qres.FinishedEvent -= OnFinished;
196 qres.CancelledEvent -= OnCancelled;
198 qres.Dispose ();
202 const string NO_RESULTS = "No results.";
204 private string getResultsLabel(BT.SimpleRootTile root)
206 string label;
207 if (root.HitCollection.NumResults == 0)
208 label = NO_RESULTS;
209 else if (root.HitCollection.FirstDisplayed == 0)
210 label = String.Format ("<b>{0} results of {1}</b> are shown.",
211 root.HitCollection.LastDisplayed + 1,
212 root.HitCollection.NumResults);
213 else
214 label = String.Format ("Results <b>{0} through {1} of {2}</b> are shown.",
215 root.HitCollection.FirstDisplayed + 1,
216 root.HitCollection.LastDisplayed + 1,
217 root.HitCollection.NumResults);
218 return label;
221 public bool canForward(string sessId)
223 Resp resp = (Resp) sessionResp[sessId];
224 if (resp == null)
225 return false;
227 BT.SimpleRootTile root = resp.resultPair.rootTile;
228 return (root != null)? root.HitCollection.CanPageForward:false;
231 public string doForward(string sessId)
233 Resp resp = (Resp) sessionResp[sessId];
235 if (!canForward(sessId) || (resp == null))
236 return NO_RESULTS;
238 BT.SimpleRootTile root = resp.resultPair.rootTile;
239 if (root != null) {
240 lock (root) {
241 root.HitCollection.PageForward ();
243 bufferRenderContext bctx = resp.bufferContext;
244 bctx.init();
245 root.Render(bctx);
246 return (getResultsLabel(root) + (resp.isLocalReq ? bctx.buffer:bctx.bufferForExternalQuery));
250 return NO_RESULTS;
253 public bool canBack(string sessId)
255 Resp resp = (Resp) sessionResp[sessId];
256 if (resp == null)
257 return false;
259 BT.SimpleRootTile root = resp.resultPair.rootTile;
260 return (root != null) ? root.HitCollection.CanPageBack:false;
263 public string doBack(string sessId)
265 Resp resp = (Resp) sessionResp[sessId];
266 if (!canBack(sessId) || (resp == null))
267 return NO_RESULTS;
269 BT.SimpleRootTile root = resp.resultPair.rootTile;
270 if (root != null) {
272 lock (root) {
273 root.HitCollection.PageBack();
275 bufferRenderContext bctx = resp.bufferContext;
276 bctx.init();
277 root.Render(bctx);
278 return (getResultsLabel(root) + (resp.isLocalReq ? bctx.buffer:bctx.bufferForExternalQuery));
282 return NO_RESULTS;
285 public bool NetworkBeagleActive
287 get {return NetworkedBeagle.NetBeagleListActive;}
290 public string doQuery(webArgs wargs)
292 if (wargs.sessId == null || wargs.searchString == null || wargs.searchString == "")
293 return NO_RESULTS;
295 log.Debug("WebBackEnd: Got Search String: " + wargs.searchString);
297 Query query = new Query();
298 query.AddText (wargs.searchString);
299 if (wargs.searchSource != null && wargs.searchSource != "")
301 query.AddSource(wargs.searchSource);
302 query.AddDomain(QueryDomain.System);
304 else
305 query.AddDomain (wargs.globalSearch ? QueryDomain.Global:QueryDomain.System);
307 QueryResult qres = new QueryResult ();
309 //Note: QueryDriver.DoQuery() local invocation is used.
310 //The root tile is used only for adding hits and generating html.
311 BT.SimpleRootTile root = new BT.SimpleRootTile ();
312 root.Query = query;
313 //root.SetSource (searchSource); Do not SetSource on root!
315 ResultPair rp = new ResultPair(root);
316 bufferRenderContext bctx = new bufferRenderContext(rp);
317 Resp resp = new Resp(rp, bctx, wargs.isLocalReq);
319 AttachQueryResult (qres, resp);
321 //Add sessionId-Resp mapping
322 if (sessionResp.Contains(wargs.sessId))
323 sessionResp[wargs.sessId] = resp;
324 else
325 sessionResp.Add(wargs.sessId, resp);
327 log.Info("WebBackEnd: Starting Query for string \"{0}\"", query.QuotedText);
329 QueryDriver.DoQuery (query, qres);
331 //Wait only till we have enough results to display
332 while ((result.Contains(qres)) &&
333 (root.HitCollection.NumResults < 10))
334 Thread.Sleep(100);
336 if (root.HitCollection.IsEmpty)
337 return NO_RESULTS;
339 lock (root) {
340 root.Render(bctx);
341 return (getResultsLabel(root) + (wargs.isLocalReq ? bctx.buffer:bctx.bufferForExternalQuery));
345 public void dispatchAction (string sessId, string actionString)
347 string tile_id = null, action = null;
348 bool actionDone = false;
350 //if (actionString.StartsWith ("dynaction:")) {
352 bufferRenderContext b = ((Resp)sessionResp[sessId]).bufferContext;
353 if (b != null)
354 actionDone = b.DoAction(actionString);
357 if (actionDone)
358 return;
360 if (actionString.StartsWith ("action:")) {
362 int pos1 = "action:".Length;
363 int pos2 = actionString.IndexOf ("!");
365 if (pos2 <= 0)
366 return;
368 tile_id = actionString.Substring (pos1, pos2 - pos1);
369 action = actionString.Substring (pos2 + 1);
371 log.Debug("WebBackEnd tile_id: {0}, action: {1}", tile_id, action);
373 BT.Tile t = ((Resp)sessionResp[sessId]).GetTile (tile_id);
375 if (t == null)
376 return;
378 MethodInfo info = t.GetType().GetMethod (action,
379 BindingFlags.Public | BindingFlags.NonPublic |
380 BindingFlags.Instance, null,
381 CallingConventions.Any, new Type[] {}, null);
383 if (info == null) {
384 log.Warn ("WebBackEnd:dispatchAction couldn't find method called {0}", action);
385 return;
388 object[] attrs = info.GetCustomAttributes (false);
389 foreach (object attr in attrs) {
390 if (attr is BT.TileActionAttribute) {
391 info.Invoke (t, null);
392 return;
395 log.Warn ("WebBackEnd:dispatchAction {0} does not have the TileAction attribute", t);
398 string command = null;
399 string commandArgs = null;
401 if (actionString.StartsWith ("http://") || actionString.StartsWith ("file://")) {
402 command = "gnome-open";
403 commandArgs = "'" + actionString + "'";
405 else if (actionString.StartsWith ("mailto:")) {
406 command = "evolution";
407 commandArgs = actionString;
410 if (command != null) {
411 Process p = new Process ();
412 p.StartInfo.UseShellExecute = false;
413 p.StartInfo.FileName = command;
414 if (commandArgs != null)
415 //if (args != null)
416 p.StartInfo.Arguments = commandArgs;
417 try {
419 p.Start ();
421 catch { }
425 //////////////////////////////////////////////////////////////////////////
427 private class ResultPair {
428 private BT.SimpleRootTile _rootTile;
429 private ArrayList _hitsCopy;
431 public ResultPair(BT.SimpleRootTile rootTile) {
432 this._rootTile = rootTile;
433 _hitsCopy = ArrayList.Synchronized(new ArrayList());
436 public BT.SimpleRootTile rootTile {
437 get { return _rootTile; }
440 public ArrayList hitsCopy {
441 get { return _hitsCopy; }
445 private class Resp {
447 private ResultPair _rp;
448 private bufferRenderContext bufCtx = null;
449 private bool _localRequest;
451 private Hashtable tileTab = null;
453 public Resp(ResultPair rp, bufferRenderContext bCtx, bool isLocalReq)
455 this._rp = rp;
456 this.bufCtx = bCtx;
457 this._localRequest = isLocalReq;
459 this.tileTab = bCtx.table;
462 public ResultPair resultPair {
463 get { return _rp; }
465 public bufferRenderContext bufferContext {
466 get { return bufCtx; }
468 public bool isLocalReq {
469 get { return _localRequest; }
472 public BT.Tile GetTile (string key)
474 if (key == "")
475 return resultPair.rootTile;
477 return (Beagle.Tile.Tile) tileTab[key];
481 //////////////////////////////////////////////////////////////////////////
482 private class bufferRenderContext : BT.TileRenderContext {
484 private ResultPair _rp;
485 private Hashtable tileTable = null;
486 private Hashtable actionTable = null;
487 int actionId = 1;
489 private System.Text.StringBuilder sb;
490 private bool renderStylesDone = false;
492 public bufferRenderContext (ResultPair rp)
494 this._rp = rp;
495 this.tileTable = Hashtable.Synchronized(new Hashtable());
496 this.actionTable = new Hashtable ();
497 init();
500 public string buffer {
501 get { return sb.ToString(); }
504 public Hashtable table {
505 get { return tileTable; }
508 public string bufferForExternalQuery {
510 get {
511 //Substitute "action:_tile_id!Open" with "http://host:port/beagle?xxxx"
512 string s;
513 string[] list = sb.ToString().Split('\"');
514 for (int i = 0; i < list.Length; i++) {
516 s = list[i];
517 if (s.StartsWith("action") && s.EndsWith("!Open")) {
519 string[] s1 = s.Split(':');
520 if (s1.Length > 1) {
521 string[] s2 = s1[1].Split('!');
522 if (s2.Length > 1) {
523 BT.Tile t = (BT.Tile) table[s2[0]];
524 list[i] = WebServiceBackEnd.AccessFilter.TranslateHit(t.Hit);
525 t.Uri = new Uri(list[i]);
530 return String.Join ("\"", list);
534 public void init()
536 lock (this) {
537 sb = new StringBuilder(4096);
538 renderStylesDone = false;
539 tileTable.Clear();
540 ClearActions();
541 tileTable[_rp.rootTile.UniqueKey] = _rp.rootTile;
544 /////////////////////////////////////////////////
545 public void ClearActions ()
547 actionTable.Clear();
548 actionId = 1;
551 private string AddAction (BT.TileActionHandler handler)
553 if (handler == null)
554 return "dynaction:NULL";
555 string key = "dynaction:" + actionId.ToString ();
556 ++actionId;
557 actionTable [key] = handler;
558 return key;
561 public bool DoAction (string key)
563 BT.TileActionHandler handler = (BT.TileActionHandler) actionTable [key];
564 if (handler != null) {
565 handler ();
566 return true;
568 return false;
570 /////////////////////////////////////////////////
572 override public void Write (string markup)
574 sb.Append(markup);
577 override public void Link (string label,
578 BT.TileActionHandler handler)
580 string key = AddAction (handler);
581 Write ("<a href=\"{0}\">{1}</a>", key, label);
584 override public void Tile (BT.Tile tile)
586 tileTable [tile.UniqueKey] = tile;
588 if (!renderStylesDone) {
589 //KNV: Using static_stylesheet for now. Replace with TileCanvas logic later:
590 Write(static_stylesheet);
592 Write ("<style type=\"text/css\" media=\"screen\">");
593 TileCanvas.RenderStyles (this);
594 Write ("</style>");
596 renderStylesDone = true;
599 if (tile != null) {
601 if (tile is BT.TileHitCollection)
602 PrefetchSnippetsForNetworkHits((BT.TileHitCollection)tile);
604 tile.Render (this);
607 /////////////////////////////////////////////////
608 // Code to scan forward through result set & prefetch/cache Snippets for Network Hits
610 public int maxDisplayed = 0;
611 const int MAX_HIT_IDS_PER_REQ = 20; //Max no. of hits snippets to seek at a time
612 const int MAX_HITS_AHEAD = 40; //No. of hits ahead of lastDisplayed to scan
614 private bool tenHits = false; //Flag to do Prefetch check only every 10 hits
616 private void PrefetchSnippetsForNetworkHits(BT.TileHitCollection thc)
618 int lastDisplayed = 0;
620 if (maxDisplayed != 0)
621 lastDisplayed = thc.LastDisplayed + 1;
623 //We have cached snippets for network hits upto maxDisplayed
624 if (lastDisplayed < maxDisplayed)
625 return;
627 maxDisplayed = thc.LastDisplayed + 1;
629 //Do Prefetch check once every ten hits
630 tenHits = !tenHits;
631 if (!tenHits)
632 return;
634 if (lastDisplayed < thc.NumResults) {
636 int limit = 0;
637 ArrayList networkHits = new ArrayList();
639 if ((thc.NumResults - lastDisplayed) > MAX_HITS_AHEAD)
640 limit = lastDisplayed + MAX_HITS_AHEAD;
641 else
642 limit = thc.NumResults;
644 ArrayList hits = _rp.hitsCopy;
645 lock (hits.SyncRoot) {
647 if (limit > hits.Count)
648 limit = hits.Count;
650 log.Debug("PrefetchSnippets: Scanning result set for Network Hits from {0} to {1}", lastDisplayed, limit);
652 //Get all NetworkHits with snippets field not initialized:
653 for (int si = lastDisplayed; si < limit ; si++)
655 if ((hits[si] is NetworkHit) && (((NetworkHit)hits[si]).snippet == null))
656 networkHits.Add((NetworkHit)hits[si]);
660 log.Debug("PrefetchSnippets: Found {0} NetworkHits without snippets", networkHits.Count);
662 while (networkHits.Count > 0) {
664 ArrayList nwHitsPerNode = new ArrayList();
665 string hostnamePort = GetHostnamePort((NetworkHit)networkHits[0]);
667 //Gather NetworkHits from a specific target Networked Beagle
668 foreach (NetworkHit nh in networkHits)
670 string hnp = GetHostnamePort(nh);
671 if (hnp == null)
672 continue;
674 if (hnp.Equals(hostnamePort)) {
676 if (nwHitsPerNode.Count < MAX_HIT_IDS_PER_REQ)
677 nwHitsPerNode.Add(nh);
678 else
679 break;
683 //Remove NetworkHits for this Networked Beagle
684 int i = networkHits.Count;
685 while (--i >= 0) {
687 string hnp = GetHostnamePort((NetworkHit)networkHits[i]);
688 if ((hnp == null) || hnp.Equals(hostnamePort))
689 networkHits.RemoveAt(i);
692 if (nwHitsPerNode.Count > 0)
694 string[] f3 = hostnamePort.Split(':');
695 if (f3.Length < 2)
697 log.Warn("PrefetchSnippets: Invalid format netBeagle URI in NetworkHit");
698 continue;
700 BeagleWebService wsp = new BeagleWebService(f3[0], f3[1]);
702 string searchToken = GetSearchToken((NetworkHit)nwHitsPerNode[0]);
704 if (searchToken.Equals("beagle")) //Check if it is Older version of Beagle networking
705 searchToken = null;
707 if (searchToken != null) {
709 int[] hitIds = new int[nwHitsPerNode.Count];
710 for (int j = 0; j < hitIds.Length; j++)
711 hitIds[j] = ((NetworkHit)nwHitsPerNode[j]).Id;
713 log.Debug("PrefetchSnippets: Invoking GetSnippets on {0} for {1} hits", wsp.Hostname, nwHitsPerNode.Count);
715 ReqContext2 rc = new ReqContext2(wsp, nwHitsPerNode, thc);
716 wsp.BeginGetSnippets(searchToken, hitIds, PrefetchSnippetsResponseHandler, rc);
719 //Signal change in TileHitCollection due to addition of snippets:
720 //_rp.rootTile.HitCollection.ClearSources(null);
722 } //end while
723 } //end if
726 private static void PrefetchSnippetsResponseHandler(IAsyncResult ar)
728 ReqContext2 rc = (ReqContext2)ar.AsyncState;
730 ArrayList nwHits = rc.GetNwHits;
731 BeagleWebService wsp = rc.GetProxy;
735 Beagle.Daemon.HitSnippet[] hslist = wsp.EndGetSnippets(ar);
737 int j = 0;
738 if (hslist.Length > 0)
740 log.Debug("PrefetchSnippetsResponseHandler: Got {0} snippet responses from {1}", hslist.Length, wsp.Hostname);
742 foreach (Beagle.Daemon.HitSnippet hs in hslist) {
744 int i, hitId;
745 string snippet;
747 try {
748 hitId = hs.hitId;
749 snippet = hs.snippet;
751 catch (Exception ex2)
753 log.Warn ("Exception in WebBackEnd: PrefetchSnippetsResponseHandler(), while getting snippet from {1}\n Reason: {2} ", wsp.Hostname + ":" + wsp.Port, ex2.Message);
754 continue;
757 if ((hitId == 0) || (snippet.StartsWith(WebServiceBackEnd.InvalidHitSnippetError)))
758 continue;
760 for (i = 0; i < nwHits.Count; i++)
761 if (((NetworkHit)nwHits[i]).Id == hitId) {
763 ((NetworkHit)nwHits[i]).snippet = snippet;
764 //log.Debug("\nPrefetchSnippetsResponseHandler: URI" + j++ + "=" + ((NetworkHit)nwHits[i]).Uri.ToString() + "\n Snippet=" + snippet);
765 break;
768 if (i < nwHits.Count)
769 nwHits.RemoveAt(i);
770 } //end foreach
773 catch (Exception ex) {
774 log.Error ("Exception in WebBackEnd: PrefetchSnippetsResponseHandler() - {0} - for {1} ", ex.Message, wsp.Hostname + ":" + wsp.Port);
777 if (nwHits.Count > 0) {
778 //Possible Error in getting snippets for these hitIds
779 log.Warn("WebBackEnd/PrefetchSnippetsResponseHandler(): Didn't get Snippets for some network Hits");
781 foreach (NetworkHit nh in nwHits)
782 nh.snippet = "";
785 //Signal change in TileHitCollection due to addition of snippets:
786 rc.GetHitCollection.ClearSources(null);
789 private class ReqContext2 {
791 BT.TileHitCollection _thc;
792 BeagleWebService _wsp;
793 ArrayList _nwHits;
795 public ReqContext2(BeagleWebService wsp, ArrayList nwHits, BT.TileHitCollection thc)
797 this._thc = thc;
798 this._wsp = wsp;
799 this._nwHits = nwHits;
802 public BT.TileHitCollection GetHitCollection {
803 get { return _thc; }
806 public BeagleWebService GetProxy {
807 get { return _wsp; }
810 public ArrayList GetNwHits {
811 get { return _nwHits; }
816 private string GetSearchToken(NetworkHit nh)
818 if (nh == null)
819 return null;
821 string netUri = nh.Uri.ToString();
823 //netbeagle://164.99.153.134:8888/searchToken?http:///....
824 string[] f1, f2 = netUri.Split('?');
825 if (f2.Length > 1) {
826 f1 = f2[0].Split ('/');
827 if (f1.Length > 1)
828 return (f1[f1.Length - 1]);
830 return null;
833 private string GetHostnamePort(NetworkHit nh)
835 if (nh == null)
836 return null;
838 string netUri = nh.Uri.ToString();
840 //netbeagle://164.99.153.134:8888/searchToken?http:///....
841 string[] f1, f2 = netUri.Split('?');
842 if (f2.Length > 1) {
843 f1 = f2[0].Split ('/');
844 if (f1.Length > 1)
845 return (f1[2]);
847 return null;
850 //////////////////////////////////////////////////////////////////////////
852 string static_stylesheet = "<style type=\"text/css\" media=\"screen\"> body, html { background: white; margin: 0; padding: 0; font-family: Sans, Segoe, Trebuchet MS, Lucida, Sans-Serif; text-align: left; line-height: 1.5em; } a, a:visited { text-decoration: none; color: #2b5a8a; } a:hover { text-decoration: underline; } img { border: 0px; } table { width: 100%; border-collapse: collapse; font-size: 10px; } tr { border-bottom: 1px dotted #999999; } tr:hover { background: #f5f5f5; } tr:hover .icon { background-color: #ddddd0; } td { padding: 6px; } td.icon { background-color: #eeeee0; min-height: 80px; width: 1%; min-width: 80px; text-align: center; vertical-align: top; padding: 12px; } .icon img { max-width: 60px; padding: 4px; } .icon img[src$='.jpg'], img[src$='.jpeg'], img[src*='.thumbnails'] {// max-width: 48px; border: 1px dotted #bbb; // padding: 4px; background: #f9f9f9; } td.content { padding-left: 12px; vertical-align: top; } #hilight { background-color: #ffee66; color: #000000; padding-left: 2px; padding-right: 2px; margin-left: -2px; margin-right: -2px; } .name {font-size: 1.3em; font-weight: bold; color: black; } .date { font-size: 1em; color: black; margin-bottom: 0.6em; margin-top: 0.2em; margin-left: 16px; } .snippet {font-size: 1em; color: gray; margin-left: 16px; } .url {font-size: 1em; color: #008200; margin-left: 16px; } ul {margin-left: 16px; padding: 0px; clear: both; } .actions { font-size: 1em; } .actions li { float: left; display: block; vertical-align: middle; padding: 0; padding-left: 20px; padding-right: 12px; background: url(file:///opt/gnome/share/icons/hicolor/16x16/stock/navigation/stock_right.png) no-repeat; min-height: 16px; -moz-opacity: 0.5; } tr:hover .actions li { -moz-opacity: 1.0; } #phone { background: url(file:///opt/gnome/share/icons/hicolor/16x16/stock/generic/stock_landline-phone.png) no-repeat; } #email { background: url(file:///opt/gnome/share/icons/hicolor/16x16/stock/net/stock_mail.png) no-repeat; } #email-forward { background: url(file:///opt/gnome/share/icons/hicolor/16x16/stock/net/stock_mail-forward.png) no-repeat; } #email-reply { background: url(file:///opt/gnome/share/icons/hicolor/16x16/stock/net/stock_mail-reply.png) no-repeat; } #message { background: url(file:///opt/gnome/share/icons/hicolor/16x16/apps/im-yahoo.png) no-repeat; } #reveal { background: url(file:///opt/gnome/share/icons/hicolor/16x16/stock/io/stock_open.png) no-repeat; } td.footer { text-align: right; border-bottom: solid 1px white; } </style>";