1 /**
2  * This file is part of mozplugger a fork of plugger, for list of developers
3  * see the README file.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #define _GNU_SOURCE /* for getsid() */
25 
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <stdio.h>
30 #include <sysexits.h>
31 #include <signal.h>
32 #include <fcntl.h>
33 #include <sys/wait.h>
34 #include <errno.h>
35 #include <X11/X.h>
36 #include <X11/Xutil.h>
37 #include <X11/Xatom.h>
38 #include <sys/socket.h>
39 #include <sys/time.h>
40 #include <stdbool.h>
41 
42 #include "npapi.h"
43 #include "cmd_flags.h"
44 #include "child.h"
45 #include "debug.h"
46 #include "pipe_msg.h"
47 
48 /**
49  * Control use of semaphore in mozplugger-helper, define if one wants
50  * semaphores (rare cases doesnt work)
51  */
52 
53 #define USE_MUTEX_LOCK
54 
55 struct VictimDetails_s
56 {
57      int noWmRunning;
58      int mapped;
59      int reparented;
60      int reparentedAttemptCount;
61      Window window;
62      int borderWidth;
63      int x;
64      int y;
65      int idealWidth; /* The width the victim wants to be if unconstrained */
66      int idealHeight; /* The height the victim wants to be if unconstrained */
67      int currentWidth;
68      int currentHeight;
69 };
70 
71 typedef struct VictimDetails_s VictimDetails_t;
72 
73 struct ParentDetails_s
74 {
75      Window window;
76      int width;
77      int height;
78 };
79 
80 typedef struct ParentDetails_s ParentDetails_t;
81 
82 struct SwallowMutex_s
83 {
84     Atom xObj;
85     int taken;
86     Window win;
87     Display * dpy;
88 };
89 
90 typedef struct SwallowMutex_s SwallowMutex_t;
91 
92 struct SigGlobals_s
93 {
94     Display * dpy;
95 #ifdef USE_MUTEX_LOCK
96     SwallowMutex_t * mutex;
97 #endif
98     pid_t childPid;
99 };
100 
101 typedef struct SigGlobals_s SigGlobals_t;
102 
103 /**
104  * Global variables
105  */
106 
107 static SigGlobals_t sig_globals;
108 static VictimDetails_t victimDetails;
109 static ParentDetails_t parentDetails;
110 static int pipe_fd;
111 static int flags;
112 static char * winname;
113 static char * file;
114 
115 static XWindowAttributes wattr;
116 
117 #define MAX_POSS_VICTIMS 100
118 static unsigned int possible_victim_count = 0;
119 static Window possible_victim_windows[MAX_POSS_VICTIMS];
120 
121 
122 /**
123  * Callback from X when there is an error. If debug defined error message is
124  * printed to debug log, otherise nothing is done
125  *
126  * @param[in] dpy The display pointer
127  * @param[in] err Pointer to the error event data
128  *
129  * @return Always returns zero
130  */
error_handler(Display * dpy,XErrorEvent * err)131 static int error_handler(Display *dpy, XErrorEvent *err)
132 {
133      char buffer[1024];
134      XGetErrorText(dpy, err->error_code, buffer, sizeof(buffer));
135      D("!!!ERROR_HANDLER!!!: %s\n", buffer);
136      return 0;
137 }
138 
139 #ifdef USE_MUTEX_LOCK
140 /**
141  * Initialise semaphore (swallow mutex semaphore protection)
142  *
143  * @param[in] dpy The display
144  * @param[in] win The window
145  * @param[out] mutex The Mutex initialised
146  *
147  * @return none
148  */
initSwallowMutex(Display * dpy,Window win,SwallowMutex_t * mutex)149 static void initSwallowMutex(Display * dpy, Window win, SwallowMutex_t * mutex)
150 {
151      D("Initialising Swallow Mutex\n");
152      if(dpy == 0)
153      {
154 	  fprintf(stderr, "Display not set so cannot initialise semaphore!\n");
155           mutex->dpy = NULL;
156      }
157      else
158      {
159           mutex->dpy = dpy;
160           mutex->win = win;
161           mutex->xObj = XInternAtom(dpy, "MOZPLUGGER_SWALLOW_MUTEX", 0);
162           mutex->taken = 0;
163      }
164 }
165 #endif
166 
167 /**
168  * Initialise the X atom used to mark windows as owned by mozplugger
169  *
170  * @param[in] dpy The display pointer
171  *
172  * @return None
173  */
getWindowOwnerMarker(Display * dpy)174 static Atom getWindowOwnerMarker(Display * dpy)
175 {
176      static Atom mark = 0;
177      if(mark == 0)
178      {
179           mark = XInternAtom(dpy, "MOZPLUGGER_OWNER", 0);
180      }
181      return mark;
182 }
183 
184 /**
185  * Get Host ID - construct a host ID using host name as source of information
186  *
187  * @return The host ID
188  */
getHostId(void)189 static uint32_t getHostId(void)
190 {
191      char hostName[128];
192      uint32_t id;
193      int i;
194 
195      memset(hostName, 0, sizeof(hostName));
196      gethostname(hostName, sizeof(hostName)-1);
197 
198      D("Host Name = \"%s\"\n", hostName);
199 
200      /* OK, Create a 32 bit hash value from the host name....
201        use this as a host ID, the possiblity of a collison of hash keys
202        effecting the swallow of victims is so infinitimisally small! */
203 
204      id = 0;
205      for(i=0; i < (int)(sizeof(hostName)/sizeof(uint32_t)); i++)
206      {
207           id = ((id << 5) ^ (id >> 27)) ^ ((uint32_t *)hostName)[i];
208      }
209      return id;
210 }
211 
212 /**
213  * Extract host Id and pid from window property. This is common code used in
214  * various places and is factored out due to complexity of 64 bit versus
215  * 32 bit machines.
216  *
217  * @param[in] dpy The display pointer
218  * @param[in] w The window to get property from
219  * @param[in] name The name of property to get
220  * @param[out] hostId The ID of the host currently owning the mutex
221  * @param[out] pid The process ID of the mutex
222  *
223  * @return 1(true) if owner found, 0 otherwise
224  */
getOwnerFromProperty(Display * dpy,Window w,Atom name,uint32_t * hostId,uint32_t * pid)225 static int getOwnerFromProperty(Display * dpy, Window w, Atom name, uint32_t * hostId, uint32_t * pid)
226 {
227      unsigned long nitems;
228      unsigned long bytes;
229      int fmt;
230      Atom type;
231      unsigned char * property = NULL;
232      int success = 0;
233 
234      /* Get hold of the Host & PID that current holds the semaphore for
235        this display! - watch out for the bizarre 64-bit platform behaviour
236        where property returned is actually an array of 2 x 64bit ints with
237        the top 32bits set to zero! */
238      XGetWindowProperty(dpy, w, name,
239                        0, 2, 0, XA_INTEGER,
240                        &type, &fmt, &nitems, &bytes,
241                        &property);
242 
243      if(property)
244      {
245           D("XGetWindowProperty() passed\n");
246 
247           /* Just check all is correct! */
248 	  if((type != XA_INTEGER) || (fmt!=32) || (nitems!=2))
249           {
250 	       fprintf(stderr, "XGetWindowProperty returned bad values "
251                             "%ld,%d,%lu,%lu\n", (long) type, fmt, nitems,
252                             bytes);
253           }
254 	  else
255 	  {
256                *hostId = (uint32_t)((unsigned long *)property)[0];
257                *pid = (uint32_t)((unsigned long *)property)[1];
258                success = 1;
259           }
260 	  XFree(property);
261      }
262      else
263      {
264           D("XGetWindowProperty() failed\n");
265      }
266      return success;
267 }
268 
269 #ifdef USE_MUTEX_LOCK
270 /**
271  * Get owner of mutex semaphore.  Returns the current owner of the semaphore
272  *
273  * @param[in] mutex The Swallow mutex structure
274  * @param[out] hostId The ID of the host currently owning the mutex
275  * @param[out] pid The process ID of the mutex
276  *
277  * @return 1(true) if owner found, 0 otherwise
278  */
getSwallowMutexOwner(const SwallowMutex_t * mutex,uint32_t * hostId,uint32_t * pid)279 static int getSwallowMutexOwner(const SwallowMutex_t * mutex,
280                                              uint32_t * hostId, uint32_t * pid)
281 {
282      return getOwnerFromProperty(mutex->dpy, mutex->win, mutex->xObj, hostId, pid);
283 }
284 
285 /**
286  * Set owner of mutex semaphore
287  *
288  * @param[in,out] mutex The swallow mutex structure
289  * @param[in] hostId The ID of the new owner of the mutex
290  * @param[in] pid The process ID of the mutex
291  *
292  * @return none
293  */
setSwallowMutexOwner(SwallowMutex_t * mutex,uint32_t hostId,uint32_t pid)294 static void setSwallowMutexOwner(SwallowMutex_t * mutex,
295                                                  uint32_t hostId, uint32_t pid)
296 {
297      unsigned long temp[2] = {hostId, pid};
298 
299      D("Setting swallow mutex owner, hostId = 0x%08X, pid=%u\n",
300                                             (unsigned) hostId, (unsigned) pid);
301      XChangeProperty(mutex->dpy, mutex->win, mutex->xObj,
302                         XA_INTEGER, 32, PropModeAppend,
303                         (unsigned char*) (&temp), 2);
304 }
305 
306 /**
307  * Take mutex semaphore ownership.
308  *
309  * @param[in,out] mutex The swallow mutex structure
310  *
311  * @return none
312  */
takeSwallowMutex(SwallowMutex_t * mutex)313 static void takeSwallowMutex(SwallowMutex_t * mutex)
314 {
315      int countDown;
316      const uint32_t ourPid = (uint32_t)getpid();
317      const uint32_t ourHostId = getHostId();
318      uint32_t otherPid;
319      uint32_t otherHostId;
320      uint32_t prevOtherPid = 0;
321 
322      if(!mutex || (mutex->dpy == 0))
323      {
324 	  return;
325      }
326 
327      D("Attempting to take Swallow Mutex\n");
328      /* Try up tp forty times ( 40 * 250ms = 10 seconds) to take
329        the semaphore... */
330 
331      countDown = 40;
332      while(1)
333      {
334 
335           /* While someone owns the semaphore */
336           while(getSwallowMutexOwner(mutex, &otherHostId, &otherPid))
337           {
338                if( otherHostId == ourHostId)
339                {
340                     if(otherPid == ourPid)
341                     {
342  	                /* Great we have either successfully taken the semaphore
343                            OR (unlikely) we previously had the semaphore! Exit
344                            the function...*/
345 	                 mutex->taken = 1;
346                          D("Taken Swallow Mutex\n");
347 		         return;
348                     }
349 
350      	    	    D("Semaphore currently taken by pid=%ld\n", (long) otherPid);
351  		    /* Check that the process that has the semaphore exists...
352         	     Bit of a hack, I cant find a function to directly check
353                      if process exists. */
354             	    if( (getsid(otherPid) < 0 ) && (errno == ESRCH))
355 	            {
356 	    	         D("Strange other Pid(%lu) cannot be found\n",(unsigned long) otherPid);
357                          XDeleteProperty(mutex->dpy, mutex->win, mutex->xObj);
358                          break; /* OK force early exit of inner while loop */
359                     }
360 	       }
361 
362                /* Check if the owner of semaphore hasn't recently changed if it
363                 has restart timer */
364                if(prevOtherPid != otherPid)
365                {
366    	            D("Looks like semaphore's owner has changed pid=%ld\n",
367                         (long) otherPid);
368 	            countDown = 40;
369                     prevOtherPid = otherPid;
370                }
371 
372                /* Do one step of the timer... */
373 	       countDown--;
374                if(countDown <= 0)
375                {
376 	    	    D("Waited long enough for Pid(%lu)\n", (unsigned long) otherPid);
377                     XDeleteProperty(mutex->dpy, mutex->win, mutex->xObj);
378 		    break;
379                }
380 
381                usleep(250000); /* 250ms */
382  	  }
383 
384           /* else no one has semaphore, timeout, or owner is dead -
385            Set us as the owner, but we need to check if we weren't
386            beaten to it so once more around the loop.
387            Note even doing the recheck does not work in 100% of all
388            cases due to task switching occuring at just the wrong moment
389            see Mozdev bug 20088 - the fix is done use the stillHaveMutex
390            function */
391 
392           setSwallowMutexOwner(mutex, ourHostId, ourPid);
393      }
394 }
395 
396 /**
397  * Check if we still have the mutex semaphore
398  *
399  * @param[in,out] mutex The swallow mutex structure
400  *
401  * @return true if we still have the Mutex semaphore
402  */
stillHaveMutex(SwallowMutex_t * mutex)403 static int stillHaveMutex(SwallowMutex_t * mutex)
404 {
405      uint32_t otherPid;
406      uint32_t otherHostId;
407 
408      if(getSwallowMutexOwner(mutex, &otherHostId, &otherPid))
409      {
410           const uint32_t ourHostId = getHostId();
411           if( otherHostId == ourHostId)
412           {
413                const uint32_t ourPid = (uint32_t)getpid();
414                if(otherPid == ourPid)
415                {
416                     return 1;
417                }
418           }
419      }
420      D("Race condition detected, semaphore pinched by Pid(%lu)\n",
421                                                       (unsigned long) otherPid);
422      return 0;
423 }
424 
425 /**
426  * Gives away ownership of the mutex semaphore
427  *
428  * @param[in,out] mutex The swallow mutex structure
429  *
430  * @return none
431  */
giveSwallowMutex(SwallowMutex_t * mutex)432 static void giveSwallowMutex(SwallowMutex_t * mutex)
433 {
434      if(!mutex || (mutex->dpy == 0))
435      {
436 	  return;
437      }
438      D("Giving Swallow Mutex\n");
439      XDeleteProperty(mutex->dpy, mutex->win, mutex->xObj);
440      mutex->taken = 0;
441 }
442 #endif
443 
444 /**
445  * Mark the victim window with a property that indicates that this instance
446  * of mozplugger-helper has made a claim to this victim. This is used to
447  * guard against two instances of mozplugger-helper claiming the same victim.
448  *
449  * @param[in] dpy The display pointer
450  * @param[in] w The window
451  *
452  * @return True(1) if taken, False(0) if not
453  */
chkAndMarkVictimWindow(Display * dpy,Window w)454 static int chkAndMarkVictimWindow(Display * dpy, Window w)
455 {
456      unsigned long temp[2];
457      uint32_t ourPid;
458      uint32_t ourHostId;
459 
460      uint32_t pid;
461      uint32_t hostId;
462      int gotIt = 0;
463 
464      Atom windowOwnerMark = getWindowOwnerMarker(dpy);
465 
466      /* See if some other instance of Mozplugger has already 'marked' this
467       * window */
468      if(getOwnerFromProperty(dpy, w, windowOwnerMark, &hostId, &pid))
469      {
470           return 0; /* Looks like someone else has marked this window */
471      }
472 
473      /* OK lets claim it for ourselves... */
474      ourPid = (uint32_t)getpid();
475      ourHostId = getHostId();
476 
477      temp[0] = ourHostId;
478      temp[1] = ourPid;
479 
480      XChangeProperty(dpy, w, windowOwnerMark,
481                      XA_INTEGER, 32, PropModeAppend,
482                      (unsigned char*) (&temp), 2);
483 
484      /* See if we got it */
485      if(getOwnerFromProperty(dpy, w, windowOwnerMark, &hostId, &pid))
486      {
487           if( (pid == ourPid) && (hostId == ourHostId) )
488           {
489                gotIt = 1;
490           }
491      }
492      else
493      {
494           D("Strange, couldn't set windowOwnerMark\n");
495           gotIt = 1;
496      }
497 
498      return gotIt;
499 }
500 
501 /**
502  * Calculate highest common factor
503  *
504  * @param[in] a First value
505  * @param[in] b Second value
506  *
507  * @return Scaling factor
508  */
gcd(int a,int b)509 static int gcd(int a, int b)
510 {
511      if (a < b)
512      {
513           return gcd(b,a);
514      }
515      if (b == 0)
516      {
517           return a;
518      }
519      return gcd(b, a % b);
520 }
521 
522 /**
523  * Adjust window size
524  *
525  * @param[in] dpy The display pointer
526  *
527  * @return none
528  */
adjust_window_size(Display * dpy)529 static void adjust_window_size(Display * dpy)
530 {
531      int x = 0;
532      int y = 0;
533      int w = parentDetails.width;
534      int h = parentDetails.height;
535 
536 
537      if (flags & H_FILL)
538      {
539 	  D("Resizing window 0x%x with FILL\n", (unsigned) victimDetails.window);
540      }
541      else if (flags & H_MAXASPECT)
542      {
543           const int d = gcd(victimDetails.idealWidth, victimDetails.idealHeight);
544           int xaspect = victimDetails.idealWidth / d;
545           int yaspect = victimDetails.idealHeight / d;
546 
547 	  if (xaspect && yaspect)
548 	  {
549                int tmpw, tmph;
550 
551 	       D("Resizing window 0x%x with MAXASPECT\n",
552                                                (unsigned) victimDetails.window);
553 	       tmph = h / yaspect;
554 	       tmpw = w / xaspect;
555 	       if (tmpw < tmph)
556                {
557                     tmph = tmpw;
558                }
559 	       tmpw = tmph * xaspect;
560 	       tmph = tmph * yaspect;
561 
562 	       x = (w - tmpw) / 2;
563 	       y = (h - tmph) / 2;
564 
565 	       w = tmpw;
566 	       h = tmph;
567 	  }
568 	  else
569 	  {
570 	       D("Not resizing window\n");
571 	       return;
572 	  }
573      }
574 
575      /* Compensate for the Victims border width (usually set to zero anyway) */
576      w -= 2 * victimDetails.borderWidth;
577      h -= 2 * victimDetails.borderWidth;
578 
579      D("New size: %dx%d+%d+%d\n", w, h, x, y);
580 
581      if((victimDetails.x == x) && (victimDetails.y == y)
582         && (victimDetails.currentWidth == w) && (victimDetails.currentHeight == h))
583      {
584           XEvent event;
585           D("No change in window size so sending ConfigureNotify instead\n");
586           /* According to X11 ICCCM specification, a compliant window
587            * manager, (or proxy in this case) should sent a ConfigureNotify
588            * event to the client even if no size change has occurred!
589            * The code below is taken and adapted from the
590            * TWM window manager and fixed movdev bug #18298. */
591           event.type = ConfigureNotify;
592           event.xconfigure.display = dpy;
593           event.xconfigure.event = victimDetails.window;
594           event.xconfigure.window = victimDetails.window;
595           event.xconfigure.x = x;
596           event.xconfigure.y = y;
597           event.xconfigure.width = w;
598           event.xconfigure.height = h;
599           event.xconfigure.border_width = victimDetails.borderWidth;
600           event.xconfigure.above = Above;
601           event.xconfigure.override_redirect = False;
602 
603           XSendEvent(dpy, victimDetails.window, False, StructureNotifyMask,
604                   &event);
605      }
606 
607      /* Always resize the window, even when the target size has not changed.
608 	This is needed for gv. */
609      XMoveResizeWindow(dpy, victimDetails.window,
610 		       x, y, (unsigned)w, (unsigned)h);
611      victimDetails.x = x;
612      victimDetails.y = y;
613      victimDetails.currentWidth = w;
614      victimDetails.currentHeight = h;
615 }
616 
617 /**
618  * Change the leader of the window. Peter - not sure why this code is needed
619  * as it does say in the ICCCM that only client applications should change
620  * client HINTS. Also all the window_group does is hint to the window manager
621  * that there is a group of windows that should be handled together.
622  *
623  * @param[in] dpy The display pointer
624  * @param[in] win The window ID
625  * @param[in] newLeader The new window leader
626  *
627  * @return none
628  */
change_leader(Display * dpy,Window win,Window newLeader)629 static void change_leader(Display * dpy, Window win, Window newLeader)
630 {
631      XWMHints *leader_change;
632      D("Changing leader of window 0x%x\n", (unsigned) win);
633 
634      if ((leader_change = XGetWMHints(dpy, win)))
635      {
636           if((leader_change->flags & WindowGroupHint) != 0)
637           {
638 		D("Old window leader was 0x%x\n",
639                                         (unsigned) leader_change->window_group);
640           }
641           if((leader_change->flags & InputHint) != 0)
642           {
643 		D("Input hint = %i\n", leader_change->input);
644           }
645           if((leader_change->flags & StateHint) != 0)
646           {
647 		D("InitialState hint = %i\n",
648                                        (unsigned) leader_change->initial_state);
649           }
650 
651 	  leader_change->flags |= WindowGroupHint;
652 	  leader_change->window_group = newLeader;
653 
654 	  D("New window leader is 0x%x\n",
655                                         (unsigned) leader_change->window_group);
656 	  XSetWMHints(dpy, win,leader_change);
657 	  XFree(leader_change);
658      }
659      else
660      {
661 	  D("XGetWMHints returned NULL\n");
662      }
663 }
664 
665 /**
666  * Reparent the window, a count is kept of the number of attempts. If this
667  * exceeds 10, give up (i.e. avoid two instances of helper fighting over
668  * the same application). Originally this was done with a semaphore, but this
669  * caused more problems than it fixed. So alternative is to assume that it is
670  * such an unlikely occurance that two instances of helper will run at exactly
671  * the same time and try to get the same window, that we give up.
672  *
673  * @param[in] dpy The display pointer
674  *
675  * @return none
676  */
reparent_window(Display * dpy)677 static void reparent_window(Display * dpy)
678 {
679      if (!victimDetails.window)
680      {
681           D("reparent_window: No victim to reparent\n");
682           return;
683      }
684      if (!parentDetails.window)
685      {
686           D("reparent_window: No parent to reparent to\n");
687           return;
688      }
689 
690      if(victimDetails.reparentedAttemptCount > 10)
691      {
692           D("Giving up reparenting to avoid - tried 10 times\n");
693           return;
694      }
695 
696      victimDetails.reparentedAttemptCount++;
697 
698 
699      D("Reparenting window 0x%x into 0x%x\n", (unsigned)victimDetails.window,
700                                                 (unsigned)parentDetails.window);
701 
702      XReparentWindow(dpy, victimDetails.window, parentDetails.window, 0, 0);
703 }
704 
705 /**
706  * Traditionally strcmp returns -1, 0 and +1 depending if name comes before
707  * or after windowname, this function also returns +1 if error
708  *
709  * @param[in] windowname The window name to compare against
710  * @param[in] name The name to compare against window name
711  *
712  * @return 0 if match, -1/+1 if different
713  */
my_strcmp(const char * windowname,const char * name)714 static int my_strcmp(const char *windowname, const char *name)
715 {
716      if (!name)
717      {
718           return 1;
719      }
720      if (!windowname)
721      {
722           return 1;
723      }
724 
725      switch (name[0])
726      {
727      case '=':
728 	  return strcmp(name+1, windowname);
729      case '~':
730 	  return strcasecmp(name+1, windowname);
731      case '*':
732 	  return strncasecmp(name+1, windowname, strlen(name)-1);
733      default:
734           /* Return 0 for success so need to invert logic as strstr
735              returns pointer to the match or NULL if no match */
736 	  return !strstr(windowname, name);
737      }
738 }
739 
740 /**
741  * Check name against the name of the passed window
742  *
743  * @param[in] dpy The display pointer
744  * @param[in] w The window to compare against
745  * @param[in] name The name to compare against window name
746  *
747  * @return 1 if match, 0 if not
748  */
check_window_name(Display * dpy,Window w,const char * name)749 static char check_window_name(Display * dpy, Window w, const char *name)
750 {
751      char * windowname;
752      XClassHint windowclass;
753 
754      if (XFetchName(dpy, w, &windowname))
755      {
756 	  const char match = (my_strcmp(windowname, name) == 0);
757 
758           D("XFetchName, checking window NAME 0x%x (%s %s %s)\n",
759                             (unsigned)w, windowname, match ? "==" : "!=", name);
760 	  XFree(windowname);
761 	  if (match)
762           {
763 	       return 1;
764 	  }
765      }
766      else
767      {
768           D("XFetchName, window has no NAME\n");
769      }
770 
771      if (XGetClassHint(dpy, w, &windowclass))
772      {
773 	  const char match = (my_strcmp(windowclass.res_name, name) == 0);
774 
775           D("XGetClassHint, checking window CLASS 0x%x (%s %s %s)\n",
776                   (unsigned)w, windowclass.res_name, match ? "==" : "!=", name);
777 	  XFree(windowclass.res_class);
778 	  XFree(windowclass.res_name);
779 
780           return match;
781      }
782      else
783      {
784           D("XGetClassHint, window has no CLASS\n");
785      }
786      return 0;
787 }
788 
789 /**
790  * Setup display ready for any reparenting of the application window. If the
791  * WINDOW is NULL (can happen see mozdev bug #18837), then return failed
792  * code from this function to indicate no window available. This then means
793  * no swallowing will occur (even if requested).
794  *
795  * @return The display pointer or NULL
796  */
setup_display(void)797 static Display * setup_display(void)
798 {
799      Display * dpy = NULL;
800      char * displayname;
801 
802      if(parentDetails.window == 0)       /* mozdev bug #18837 */
803      {
804           D("setup_display() WINDOW is Null - so nothing setup\n");
805           return NULL;
806      }
807 
808      displayname = getenv("DISPLAY");
809      D("setup_display(%s)\n", displayname);
810 
811      XSetErrorHandler(error_handler);
812 
813      dpy = XOpenDisplay(displayname);
814      if(dpy == 0)
815      {
816           D("setup_display() failed cannot open display!!\n");
817 	  return 0;
818      }
819 
820      if (!XGetWindowAttributes(dpy, parentDetails.window, &wattr))
821      {
822           D("setup_display() failed cannot get window attributes!!\n");
823           XCloseDisplay(dpy);
824           return NULL;
825      }
826      D("display=0x%x\n", (unsigned) dpy);
827      D("WINDOW =0x%x\n", (unsigned) parentDetails.window);
828      D("rootwin=0x%x\n", (unsigned) wattr.root);
829 
830      D("setup_display() done\n");
831 
832      return dpy;
833 }
834 
835 /**
836  * Add to list of possible victim windows. Called whenever we get a
837  * CREATE_NOTIFY on the root window.
838  *
839  * @param[in] window The window
840  *
841  * @return none
842  */
add_possible_victim(Window window)843 static void add_possible_victim(Window window)
844 {
845      possible_victim_windows[possible_victim_count] = window;
846      if(possible_victim_count < MAX_POSS_VICTIMS)
847      {
848           possible_victim_count++;
849      }
850 }
851 
852 /**
853  * Checks if the passed window is the victim.
854  *
855  * @param[in] dpy The display pointer
856  * @param[in] window The window ID
857  * @param[in] mutex The Swallow mutex
858  *
859  * @return True if found our victim for first time.
860  */
find_victim(Display * dpy,Window window,SwallowMutex_t * mutex)861 static int find_victim(Display * dpy, Window window, SwallowMutex_t * mutex)
862 {
863      if (!victimDetails.window)
864      {
865           int i;
866           Window found = 0;
867 
868 	  D("Looking for victim... (%s)\n", winname);
869 
870           /* New way, check through list of newly created windows */
871 	  for(i = 0; i < possible_victim_count; i++)
872           {
873                if(window == possible_victim_windows[i])
874 	       {
875                     if (check_window_name(dpy, window, winname))
876                     {
877                          found = true;
878                          break;
879                     }
880                }
881           }
882 
883 	  if (found)
884 	  {
885 	       XWindowAttributes ca;
886 	       if(XGetWindowAttributes(dpy, window, &ca))
887                {
888                     /* See if some instance of mozplugger got the window
889                      * before us, if not mark it as ours */
890                     if(chkAndMarkVictimWindow(dpy, window))
891                     {
892                          victimDetails.window = window;
893                          victimDetails.borderWidth = ca.border_width;
894                          victimDetails.x = ca.x;
895                          victimDetails.y = ca.y;
896                          victimDetails.idealWidth = ca.width;
897                          victimDetails.idealHeight = ca.height;
898                          victimDetails.currentWidth = ca.width;
899                          victimDetails.currentHeight = ca.height;
900 
901       	                 D("Found victim=0x%x, x=%i, y=%i, width=%i, height=%i, "
902                                                                   "border=%i\n",
903                                           (unsigned) victimDetails.window,
904                                                      victimDetails.x,
905                                                      victimDetails.y,
906                                                      victimDetails.idealWidth,
907                                                      victimDetails.idealHeight,
908                                                      victimDetails.borderWidth);
909 
910                          /* To avoid losing events, enable monitoring events on
911                           * victim at earlist opportunity */
912 
913                          XSelectInput(dpy, victimDetails.window,
914                                              StructureNotifyMask | FocusChangeMask
915                                            | EnterWindowMask | LeaveWindowMask);
916                          XSync(dpy, False);
917 
918                          XSelectInput(dpy, wattr.root, 0);
919 
920 #ifdef USE_MUTEX_LOCK
921 	                 giveSwallowMutex(mutex);
922 #endif
923                          return true;
924                     }
925                     else
926                     {
927                          D("Window 0x%x already claimed by another"
928                                 " instance of mozplugger\n", (unsigned) window);
929                     }
930                }
931                else
932                {
933                     D("XGetWindowAttributes failed for 0x%x\n",
934                                                              (unsigned) window);
935                }
936 	  }
937      }
938      return false;
939 }
940 
941 /**
942  * handle X event for the root window. Mozplugger in effect is acting like a
943  * window manager by intercepting root window events. The one event it would
944  * like to intercept, but cannot is the MapRequest. It cannot because only one
945  * client is allowed to intercept that event (i.e. the real window manager). When
946  * the real window manager receives the MapRequest, it will most likely try and
947  * reparent that window into a frame containing decorations (i.e. title bar,
948  * etc). The only way mozplugger knows this has happened is when it sees the
949  * ReparentNotify. Ideally Mozplugger needs to over-ride this reparenting, the
950  * current solution is to reparent that window again. Result is a fight with
951  * window manager. The alternative is to set the override_redirect attribute
952  * on the window (this stops the MapRequest event). But according to ICCCM this
953  * is bad practice, so mozplugger only does it as a last resort (see
954  * reparent_window function above).
955  *
956  * An alternative would be to reparent the window before the MapRequest i.e.
957  * when the window is invisible. Unfortunately some apps create many invisible
958  * root windows and it is difficult to know which window is the one to grab until
959  * the application makes it clear by Mapping that window.
960  *
961  * @param[in] dpy The display pointer
962  * @param[in] rootWin The window ID
963  * @param[in] ev the event
964  * @param[in] mutex The swallow mutex
965  *
966  * @return none
967  */
handle_rootWindow_event(Display * dpy,Window rootWin,const XEvent * const ev,SwallowMutex_t * mutex)968 static void handle_rootWindow_event(Display * dpy, Window rootWin, const XEvent * const ev,
969                                                         SwallowMutex_t * mutex)
970 {
971      switch (ev->type)
972      {
973      case CreateNotify:
974           D("***CreateNotify for root, window=0x%x, "
975                                             "override_redirect=%i\n",
976                                             (unsigned) ev->xcreatewindow.window,
977                                            ev->xcreatewindow.override_redirect);
978           /* All toplevel new app windows will be created on desktop (root),
979            * so lets keep a list as they are created */
980           add_possible_victim(ev->xcreatewindow.window);
981 	  break;
982 
983      case MapNotify:
984           D("***MapNotify for root, window=0x%x, override_redirect=%i\n",
985                                                     (unsigned) ev->xmap.window,
986                                                     ev->xmap.override_redirect);
987           /* A window has been mapped, if window manager is allowed to
988            * fiddle, lets see if this is the window */
989           if(!ev->xmap.override_redirect)
990           {
991                Window wnd = ev->xmap.window;
992                if(find_victim(dpy, wnd, mutex))
993                {
994                     /* If we get MapNotify on root before ReparentNotify for our
995                      * victim this means either:-
996                      * (1) There is no reparenting window manager running
997                      * (2) We are running over NX
998                      * With NX, we get the MapNotify way too early! (window not
999                      * mapped yet on the server), so need to be clever?
1000                      * Set flag, this will be used later in the select loop */
1001                     D("Looks like no reparenting WM running\n");
1002 		    change_leader(dpy, victimDetails.window, rootWin);
1003                     victimDetails.noWmRunning = true;
1004 		    reparent_window(dpy);
1005                }
1006           }
1007 	  break;
1008 
1009 
1010      case ReparentNotify:
1011           D("***ReparentNotify for root, parent=0x%x, window=0x%x, "
1012                                            "x=%i, y=%i, override_redirect=%i\n",
1013                                                (unsigned) ev->xreparent.parent,
1014                                                (unsigned) ev->xreparent.window,
1015                                                ev->xreparent.x, ev->xreparent.y,
1016 					       ev->xreparent.override_redirect);
1017 
1018           /* A window has been reparented, if window manager is allowed to
1019            * fiddle, lets see if this is the window we are looking for */
1020           if(!ev->xreparent.override_redirect)
1021           {
1022                Window wnd = ev->xreparent.window;
1023 
1024                if(find_victim(dpy, wnd, mutex))
1025                {
1026                     /* Avoid the fight with window manager to get reparent,
1027                      * instead be kind and withdraw window and wait for WM
1028                      * to reparent window back to root */
1029                    D("Withdraw window 0x%x\n", (unsigned) wnd);
1030 
1031                    XWithdrawWindow(dpy, wnd, DefaultScreen(dpy));
1032 #if 0
1033 		   change_leader(dpy, victimDetails.window, rootWin);
1034                    reparent_window(dpy);
1035 #endif
1036                }
1037           }
1038 	  break;
1039 
1040 
1041      case ConfigureNotify:
1042           D("***ConfigureNotify for root, window=0x%x, "
1043                       "x=%i, y=%i, width=%i, height=%i, override_redirect=%i\n",
1044                                               (unsigned) ev->xconfigure.window,
1045                                               ev->xconfigure.x, ev->xconfigure.y,
1046                                               ev->xconfigure.width,
1047                                               ev->xconfigure.height,
1048 					      ev->xconfigure.override_redirect);
1049           break;
1050 
1051 
1052 
1053      case UnmapNotify:
1054 	  D("***UnmapNotify for root, window=0x%x, from_configure=%i, "
1055                                 "send_event=%i\n", (unsigned) ev->xunmap.window,
1056                                                       ev->xunmap.from_configure,
1057                                                          ev->xunmap.send_event);
1058           break;
1059 
1060 
1061      case DestroyNotify:
1062           D("***DestroyNotify for root, window=0x%x\n",
1063                                           (unsigned) ev->xdestroywindow.window);
1064           break;
1065 
1066      case ClientMessage:
1067           D("***ClientMessage for root\n");
1068           break;
1069 
1070      default:
1071           D("!!Got unhandled event for root->%d\n", ev->type);
1072           break;
1073      }
1074 }
1075 
1076 /**
1077  * handle X event for the victim window
1078  *
1079  * @param[in] dpy The display pointer
1080  * @param[in] ev the event
1081  *
1082  * @return none
1083  */
handle_victimWindow_event(Display * dpy,const XEvent * const ev)1084 static void handle_victimWindow_event(Display * dpy, const XEvent * const ev)
1085 {
1086      switch (ev->type)
1087      {
1088      case UnmapNotify:
1089 	  D("UNMAPNOTIFY for victim, window=0x%x, from_configure=%i, "
1090                                 "send_event=%i\n", (unsigned) ev->xunmap.window,
1091                                                      ev->xunmap.from_configure,
1092                                                      ev->xunmap.send_event);
1093 	  victimDetails.mapped = false;
1094           break;
1095 
1096      case MapNotify:
1097           D("MAPNOTIFY for victim, window=0x%x, override_redirect=%i\n",
1098                                                     (unsigned) ev->xmap.window,
1099                                                     ev->xmap.override_redirect);
1100           victimDetails.mapped = true;
1101 	  if(victimDetails.reparented)
1102           {
1103 	       adjust_window_size(dpy);
1104           }
1105           break;
1106 
1107      case ReparentNotify:
1108           {
1109                const XReparentEvent * evt = &ev->xreparent;
1110 
1111                if (evt->parent == parentDetails.window)
1112                {
1113  	             D("REPARENT NOTIFY on victim to the right window, "
1114 		                           "parent=0x%x, window=0x%x, "
1115                                            "x=%i, y=%i, override_redirect=%i\n",
1116                                                 (unsigned) evt->parent,
1117                                                 (unsigned) evt->window,
1118                                                evt->x, evt->y,
1119 					       evt->override_redirect);
1120 	            victimDetails.reparented = true;
1121                     if(!victimDetails.mapped)
1122                     {
1123 	                 D("XMapWindow(0x%x)\n", (unsigned) victimDetails.window);
1124                          XMapWindow(dpy, victimDetails.window);
1125                     }
1126                     else
1127                     {
1128 	                 adjust_window_size(dpy);
1129                     }
1130 	       }
1131                else
1132                {
1133 	            D("REPARENT NOTIFY on victim to some other window! "
1134 		                           "parent=0x%x, window=0x%x, "
1135                                            "x=%i, y=%i, override_redirect=%i\n",
1136                                                (unsigned) evt->parent,
1137                                                (unsigned) evt->window,
1138                                                evt->x, evt->y,
1139   					       evt->override_redirect);
1140                     victimDetails.noWmRunning = false;
1141 	            victimDetails.reparented = false;
1142                     reparent_window(dpy);
1143 	       }
1144           }
1145 	  break;
1146 
1147      case ConfigureNotify:
1148           D("CONFIGURE NOTIFY for victim, window=0x%x,"
1149                                     " x=%d, y=%d, w=%d, h=%d, "
1150                                     "border_width=%d, override_redirect=%i\n",
1151                                               (unsigned) ev->xconfigure.window,
1152                                              ev->xconfigure.x, ev->xconfigure.y,
1153                                               ev->xconfigure.width,
1154                                               ev->xconfigure.height,
1155                           		      ev->xconfigure.border_width,
1156 					      ev->xconfigure.override_redirect);
1157           break;
1158 
1159      case ClientMessage:
1160           D("CLIENT MESSAGE for victim\n");
1161                     /* I see this with evince! perhaps it needs to be handled? */
1162           break;
1163 
1164      case DestroyNotify:
1165           D("DESTROY NOTIFY for victim, window=0x%x\n",
1166                                           (unsigned) ev->xdestroywindow.window);
1167           if(ev->xdestroywindow.window == victimDetails.window)
1168           {
1169                XSelectInput(dpy, victimDetails.window, 0);
1170                victimDetails.window = 0;
1171           }
1172           break;
1173 
1174      case EnterNotify:
1175           D("ENTER NOTIFY for victim, window=0x%x\n", (unsigned) ev->xcrossing.window);
1176           if(ev->xcrossing.mode == NotifyGrab)
1177           {
1178                D("Pointer grabbed by child!!\n");
1179           }
1180           break;
1181 
1182      default:
1183           {
1184               char * name = "";
1185               switch (ev->type)
1186               {
1187                    case FocusIn: name="FOCUS IN"; break;
1188                    case FocusOut: name="FOCUS OUT"; break;
1189                    case LeaveNotify: name="LEAVE NOTIFY"; break;
1190               }
1191               D("!!Got unhandled event for victim->%s(%d)\n", name, ev->type);
1192           }
1193           break;
1194      }
1195 }
1196 
1197 /**
1198  * handle X event for the parent window
1199  *
1200  * @param[in] dpy The display Pointer
1201  * @param[in] ev the event
1202  *
1203  * @return none
1204  */
handle_parentWindow_event(Display * dpy,const XEvent * const ev)1205 static void handle_parentWindow_event(Display * dpy, const XEvent * const ev)
1206 {
1207      unsigned long mask;
1208      switch (ev->type)
1209      {
1210      case ConfigureRequest:
1211           mask = ev->xconfigurerequest.value_mask;
1212           D("ConfigureRequest to WINDOW mask=0x%lx\n", mask);
1213           if(ev->xconfigurerequest.window != victimDetails.window)
1214           {
1215                D("- Strange Configure Request not for victim\n");
1216           }
1217           else
1218           {
1219                int adjustWindow = false;
1220 
1221                if(mask & (CWHeight | CWWidth))
1222                {
1223                     D(" - request to set width & height\n");
1224 	            victimDetails.idealWidth = ev->xconfigurerequest.width,
1225                     victimDetails.idealHeight = ev->xconfigurerequest.height;
1226                     adjustWindow = true;
1227                }
1228                if(mask & (CWBorderWidth))
1229                {
1230                     const int w = ev->xconfigurerequest.border_width;
1231                     D("- request to set border width=%d\n", w);
1232                     if(victimDetails.borderWidth != w)
1233                     {
1234                          victimDetails.borderWidth = w;
1235                          adjustWindow = true;
1236                     }
1237                     XSetWindowBorderWidth(dpy, victimDetails.window,
1238                                                                   (unsigned) w);
1239                }
1240                /* Only adjust if window has been mapped and reparented */
1241                if(adjustWindow && victimDetails.mapped
1242                                                     && victimDetails.reparented)
1243                {
1244                     adjust_window_size(dpy);
1245                }
1246           }
1247 	  break;
1248 
1249      default:
1250           D("!!Got unhandled event for PARENT->%d\n", ev->type);
1251           break;
1252      }
1253 }
1254 
1255 /**
1256  * Check events from X
1257  * Read in events from X and process, keep reading in events until no more
1258  * and then exit. It is important that all events have been processed and
1259  * not left pending.
1260  *
1261  * @param[in] dpy The display pointer
1262  * @param[in] mutex The swallow mutex
1263  *
1264  * @return none
1265  */
check_x_events(Display * dpy,SwallowMutex_t * mutex)1266 static void check_x_events(Display * dpy, SwallowMutex_t * mutex)
1267 {
1268      int numEvents = XPending(dpy);
1269 
1270      /* While some events pending... Get and action */
1271      while (numEvents > 0)
1272      {
1273           XEvent ev;
1274 
1275           XNextEvent(dpy, &ev);
1276 
1277 	  if (ev.xany.window == wattr.root)
1278 	  {
1279                handle_rootWindow_event(dpy, wattr.root, &ev, mutex);
1280 	  }
1281 	  else if (victimDetails.window
1282                                && ev.xany.window == victimDetails.window)
1283 	  {
1284                handle_victimWindow_event(dpy, &ev);
1285 	  }
1286           else if (parentDetails.window
1287                                     && (ev.xany.window == parentDetails.window))
1288 	  {
1289                handle_parentWindow_event(dpy, &ev);
1290 	  }
1291           else
1292           {
1293 	       D("!!Got unhandled event for unknown->%d\n", ev.type);
1294           }
1295 
1296           /* If this is the last of this batch, check that more havent
1297            * been added in the meantime as a result of an action */
1298           numEvents--;
1299           if(numEvents == 0)
1300           {
1301                numEvents = XPending(dpy);
1302           }
1303      }
1304 }
1305 
1306 /**
1307  * Terminate, called from signal handler or when SHUTDOWN_MSG received
1308  *
1309  * @param[in] mutex The swallow mutex
1310  * @param[in] pid The process ID of the mutex
1311  * @param[in] dpy The display pointer
1312  */
terminate(SwallowMutex_t * mutex,pid_t pid,Display * dpy)1313 static void terminate(SwallowMutex_t * mutex, pid_t pid, Display * dpy)
1314 {
1315 #ifdef USE_MUTEX_LOCK
1316      giveSwallowMutex(mutex);
1317 #endif
1318 
1319      if(pid >= 0)
1320      {
1321           kill_app(pid);
1322      }
1323 
1324      if(dpy)
1325      {
1326           XCloseDisplay(dpy);
1327      }
1328 }
1329 
1330 
1331 /**
1332  * Check events from pipe connected to mozplugger
1333  * Read in events from pipe connected to mozplugger and process
1334  *
1335  * @param[in] dpy The display pointer
1336  * @param[in] mutex The swallow mutex
1337  * @param[in] pid The process ID of the mutex
1338  *
1339  * @return none
1340  */
check_pipe_fd_events(Display * dpy,SwallowMutex_t * mutex)1341 static void check_pipe_fd_events(Display * dpy, SwallowMutex_t * mutex)
1342 {
1343      struct PipeMsg_s msg;
1344      int n;
1345 
1346      Window oldwindow = parentDetails.window;
1347 
1348      n = read(pipe_fd, ((char *)&msg),  sizeof(msg));
1349      if((n == 0) || ((n < 0) && (errno != EINTR)))
1350      {
1351           D("Pipe read error, returned n=%i\n", n);
1352           terminate(mutex, sig_globals.childPid, dpy);
1353 	  exit(EX_UNAVAILABLE);
1354      }
1355 
1356      if (n != sizeof(msg))
1357      {
1358           if(n > 0)
1359           {
1360               D("Pipe msg too short, size = %i\n", n);
1361           }
1362 	  return;
1363      }
1364 
1365      switch(msg.msgType)
1366      {
1367           case WINDOW_MSG:
1368                parentDetails.window = (Window) msg.window_msg.window;
1369                parentDetails.width = (int) msg.window_msg.width;
1370                parentDetails.height = (int) msg.window_msg.height;
1371                D("Read Pipe, got WINDOW_MSG (win=0x%x - %i x %i)\n",
1372                        (unsigned) parentDetails.window, parentDetails.width, parentDetails.height);
1373                break;
1374 
1375           case SHUTDOWN_MSG:
1376                D("Read Pipe, got SHUTDOWN_MSG, terminating..\n");
1377                terminate(mutex, sig_globals.childPid, dpy);
1378                exit(EXIT_SUCCESS);
1379 
1380           default:
1381                D("Read Pipe, got unknown msg\n");
1382                return;
1383      }
1384 
1385      if (parentDetails.window && dpy )
1386      {
1387           if(parentDetails.window != oldwindow)
1388           {
1389                D("Switch parent window from 0x%x to 0x%x\n", (unsigned)oldwindow, (unsigned) parentDetails.window);
1390                victimDetails.reparented = false;
1391 
1392                /* To avoid losing events, enable monitoring events on new parent
1393                 * before disabling monitoring events on the old parent */
1394 
1395                XSelectInput(dpy, parentDetails.window, SubstructureRedirectMask | FocusChangeMask);
1396                XSync(dpy, False);
1397                XSelectInput(dpy, oldwindow, 0);
1398 
1399                if(victimDetails.window)
1400                {
1401                     reparent_window(dpy);
1402                }
1403                else
1404                {
1405                     D("Victim window not ready to be reparented\n");
1406                }
1407           }
1408 
1409           if(victimDetails.window && victimDetails.mapped && victimDetails.reparented)
1410           {
1411                /* The window has been resized. */
1412                adjust_window_size(dpy);
1413           }
1414           else
1415           {
1416                D("Victim window not ready to be adjusted\n");
1417           }
1418      }
1419 }
1420 
1421 /**
1422  * Check the app status
1423  *
1424  * @param[in] flags The command flags
1425  *
1426  * @return -2 if broken, -1
1427  */
check_app_status(unsigned flags)1428 static int check_app_status(unsigned flags)
1429 {
1430      int retVal = 1;
1431      int status = wait_child(sig_globals.childPid);
1432 
1433      switch(status)
1434      {
1435      case -2:  /* App crashed */
1436           sig_globals.childPid = -1;
1437           exit(EX_UNAVAILABLE);
1438           break;
1439 
1440      case -1: /* App exited OK */
1441           sig_globals.childPid = -1;
1442           if (!(flags & H_IGNORE_ERRORS))
1443           {
1444                exit(WEXITSTATUS(status));
1445           }
1446           break;
1447 
1448      case 0: /* App deattached */
1449           sig_globals.childPid = -1;
1450           if((flags & H_DAEMON) == 0)
1451           {
1452                retVal = -1;
1453           }
1454           else
1455           {
1456                D("Child process has detached, keep going\n");
1457                retVal = 0;
1458           }
1459      /* case 1:  App still running */
1460      }
1461      return retVal;
1462 }
1463 
1464 /**
1465  * Keep checking all events until application dies
1466  * Use select to check if any events need processing
1467  *
1468  * @param[in] dpy The display pointer
1469  * @param[in] mutex The swallow mutex
1470  *
1471  * @return none
1472  */
check_all_events(Display * dpy,SwallowMutex_t * mutex)1473 static void check_all_events(Display * dpy, SwallowMutex_t * mutex)
1474 {
1475      int sig_chld_fd = get_SIGCHLD_fd();
1476 
1477      while(1)
1478      {
1479           int rd_chld_fd = get_chld_out_fd();
1480 	  int selectRetVal;
1481           int maxfd = 0;
1482 
1483 	  struct timeval timeout;
1484           struct timeval * pTimeout = 0;
1485 
1486           fd_set fds;
1487 
1488           FD_ZERO(&fds);
1489           if (dpy)
1490           {
1491 	       check_x_events(dpy, mutex);
1492 
1493                maxfd = ConnectionNumber(dpy);
1494                FD_SET(ConnectionNumber(dpy), &fds);
1495                /* If NX is is use and nxagent is configured as rootless, it can
1496                * appear that there is no WM running, but actually there is and its
1497                * actions (reparenting to add decorations) is invisible. In this
1498                * case just reparent 4 times in a row with 1 second gaps. If the
1499                * link latency > 4 seconds this may fail? */
1500                if((victimDetails.noWmRunning)
1501                              && (victimDetails.reparentedAttemptCount < 4))
1502                {
1503                     pTimeout = &timeout;
1504                     pTimeout->tv_usec = 50000;
1505                     pTimeout->tv_sec = 0;
1506                }
1507           }
1508           maxfd = pipe_fd > maxfd ? pipe_fd : maxfd;
1509           FD_SET(pipe_fd, &fds);
1510           if(sig_chld_fd >= 0)
1511           {
1512                maxfd = sig_chld_fd > maxfd ? sig_chld_fd : maxfd;
1513                FD_SET(sig_chld_fd, &fds);
1514           }
1515           if(rd_chld_fd >= 0)
1516           {
1517                maxfd = rd_chld_fd > maxfd ? rd_chld_fd : maxfd;
1518                FD_SET(rd_chld_fd, &fds);
1519           }
1520 
1521           D("SELECT IN %s\n", pTimeout ? "with timeout" : "");
1522           selectRetVal = select(maxfd + 1, &fds, NULL, NULL, pTimeout);
1523           D("SELECT OUT\n");
1524 
1525           if(selectRetVal > 0)
1526           {
1527 	       if((pipe_fd >= 0) && FD_ISSET(pipe_fd, &fds))
1528                {
1529 	            check_pipe_fd_events(dpy, mutex);
1530                }
1531                if((sig_chld_fd >= 0) && FD_ISSET(sig_chld_fd, &fds))
1532                {
1533                     handle_SIGCHLD_event();
1534                }
1535                if((rd_chld_fd >= 0) && FD_ISSET(rd_chld_fd, &fds))
1536                {
1537                     handle_chld_out_event(rd_chld_fd);
1538                }
1539           }
1540 	  else if((selectRetVal == 0) && pTimeout)
1541           {
1542                D("Select timeout and suspect invisible WM\n");
1543                reparent_window(dpy);
1544           }
1545           else
1546           {
1547                D("Select exited unexpected, errno = %i\n", errno);
1548           }
1549 
1550           if(sig_globals.childPid >= 0)
1551           {
1552                int retVal = check_app_status(flags);
1553 	       if(retVal < 0)
1554                {
1555                     return;
1556                }
1557                if(retVal == 0)
1558                {
1559                    sig_globals.childPid = -1;
1560                }
1561           }
1562      }
1563 }
1564 
1565 /**
1566  * Handle the application
1567  * Wait for application to finish and exit helper if a problem
1568  *
1569  * @param[in] dpy The display pointer
1570  * @param[in] mutex The swallow mutex
1571  *
1572  * @return none
1573  */
handle_app(Display * dpy,SwallowMutex_t * mutex)1574 static void handle_app(Display * dpy, SwallowMutex_t * mutex)
1575 {
1576      if (flags & H_SWALLOW)
1577      {
1578           /* Whilst waiting for the Application to complete, check X events */
1579 	  check_all_events(dpy, mutex);
1580 
1581 #ifdef USE_MUTEX_LOCK
1582           /* Make sure the semaphore has been released */
1583           giveSwallowMutex(mutex);
1584 #endif
1585      }
1586      else if(flags & H_DAEMON)
1587      {
1588           /* If Daemon, then it is not supposed to exit, so we exit instead */
1589           D("App has become a daemon, so exit\n");
1590           terminate(mutex, -1, dpy);
1591           exit(EXIT_SUCCESS);
1592      }
1593      else
1594      {
1595 	  check_all_events(NULL, NULL);
1596      }
1597 
1598      sig_globals.childPid = -1;
1599      D("Exited OK!\n");
1600 }
1601 
1602 /**
1603  * Exit early due to problem. Prints a message to stderr and then exits
1604  * the application
1605  *
1606  * @return none
1607  */
exitEarly(void)1608 static void exitEarly(void)
1609 {
1610      fprintf(stderr,"MozPlugger version " VERSION " helper application.\n"
1611 		  "Please see 'man mozplugger' for details.\n");
1612      exit(EXIT_FAILURE);
1613 }
1614 
1615 /**
1616  * Expands the winname if it contains %f or %p by replacing %f by the file
1617  * name part of the URL or %p by the whole of thr URL.
1618  *
1619  * @param[in,out] buffer The data buffer to use
1620  * @param[in] bufferLen Size of buffer
1621  * @param[in] winname The window name
1622  * @param[in] file The file name associated with child
1623  *
1624  * @return none
1625  */
expand_winname(char * buffer,int bufferLen,const char * winame,const char * file)1626 static void expand_winname(char * buffer, int bufferLen, const char * winame, const char * file)
1627 {
1628      const char * p = winame;
1629      char * q = buffer;
1630      char * end = &buffer[bufferLen-1];
1631 
1632      D("winame before expansion is '%s'\n", winname);
1633 
1634      while((*p != '\0') && (q < end))
1635      {
1636           if( *p != '%')
1637           {
1638                *q++ = *p++;
1639           }
1640           else
1641           {
1642                const char * r;
1643                switch(p[1])
1644                {
1645                     case '%' :
1646                          *q++ = '%';
1647                          p += 2;
1648                          break;
1649 
1650                     case 'f' :       /* %f = insert just the filename no path */
1651                          r = strrchr(file, '/'); /* Find the filename start */
1652                          if(r == 0)
1653                          {
1654                               r = file;
1655                          }
1656                          else
1657                          {
1658                               r++; /* Skip the slash */
1659                          }
1660                          while((*r != '\0') && (q < end))
1661                          {
1662                               *q++ = *r++;
1663                          }
1664                          p += 2; /* Skip the %f */
1665                          break;
1666 
1667                     case 'p' :
1668                          r = file;
1669                          while((*r != '\0') && (q < end))
1670                          {
1671                               *q++ = *r++;
1672                          }
1673                          p += 2;
1674                          break;
1675 
1676                     default :
1677                          *q++ = *p++;
1678                          break;
1679                }
1680           }
1681      }
1682      *q = '\0';
1683 
1684      D("winame after expansion is '%s'\n", buffer);
1685 }
1686 
1687 /**
1688  * Handle the SIGTERM signal. Terminate the helper and child. Calling C-lib
1689  * functions in a signal handler is not good, so avoid if we can. Best if
1690  * mozplugger.so uses the SHUTDOWN_MSG.
1691  *
1692  * @return none
1693  */
sigTerm()1694 static void sigTerm()
1695 {
1696      D("SIGTERM received\n");
1697      terminate(sig_globals.mutex, sig_globals.childPid, sig_globals.dpy);
1698      _exit(EXIT_SUCCESS);
1699 }
1700 
1701 /**
1702  * main() - normally called from the child process started by mozplugger.so
1703  *
1704  * @param[in] argc The number of arguments
1705  * @param[in] argv List of arguments
1706  *
1707  * @return Never returns (unless app exits)
1708  */
main(int argc,char ** argv)1709 int main(int argc, char **argv)
1710 {
1711      char buffer[100];
1712 
1713      unsigned long temp = 0;
1714      int i;
1715      int repeats;
1716      Display * dpy;
1717      SwallowMutex_t mutex;
1718      char * command;
1719 
1720      D("Helper started.....\n");
1721 
1722      if (argc < 3)
1723      {
1724           exitEarly();
1725      }
1726 
1727      memset(&victimDetails, 0, sizeof(victimDetails));
1728      memset(&parentDetails, 0, sizeof(parentDetails));
1729 
1730      i = sscanf(argv[1],"%d,%d,%d,%lu,%d,%d",
1731 	    &flags,
1732 	    &repeats,
1733 	    &pipe_fd,
1734 	    &temp,
1735 	    &parentDetails.width,
1736 	    &parentDetails.height);
1737 
1738      if(i < 6)
1739      {
1740           exitEarly();
1741      }
1742 
1743      parentDetails.window = (Window)temp;
1744 
1745      command = argv[2];
1746      winname = getenv("winname");
1747      file = getenv("file");
1748 
1749      if(winname)
1750      {
1751           expand_winname(buffer, sizeof(buffer), winname, file);
1752           winname = buffer;
1753      }
1754      D("HELPER: %s %s %s %s\n",
1755        argv[0],
1756        argv[1],
1757        file,
1758        command);
1759 
1760      redirect_SIGCHLD_to_fd();
1761 
1762      /* Create handler for when terminating the helper */
1763 
1764 
1765      sig_globals.dpy = dpy = setup_display();
1766      sig_globals.mutex = NULL;
1767      sig_globals.childPid = -1;
1768 
1769      signal(SIGTERM, sigTerm);
1770 
1771      if (!dpy)
1772      {
1773          if( (flags & H_SWALLOW) != 0)
1774          {
1775 	      D("Failed to open X display - canceling swallow functionality\n");
1776 	      flags &=~ H_SWALLOW;
1777          }
1778      }
1779 
1780      if (repeats < 1)
1781      {
1782           repeats = 1;
1783      }
1784 
1785      while (repeats > 0)
1786      {
1787 	  int loops = 1;
1788 	  pid_t pid;
1789 
1790 	  /* This application will use the $repeat variable */
1791 	  if (flags & H_REPEATCOUNT)
1792           {
1793 	       loops = repeats;
1794           }
1795 
1796 	  /* Expecting the application to loop */
1797 	  if (flags & H_LOOP)
1798           {
1799 	       loops = INF_LOOPS;
1800           }
1801 
1802 	  if (flags & H_SWALLOW)
1803 	  {
1804 #ifdef USE_MUTEX_LOCK
1805 	       /* If we are swallowing a victim window we need to guard
1806 		  against more than one instance of helper running in
1807 		  parallel, this is done using a global mutex semaphore.
1808                   Although its not successful in all cases! */
1809 	       initSwallowMutex(dpy, wattr.root, &mutex);
1810                sig_globals.mutex = &mutex;
1811 
1812                /* There is a race condition when taking the semaphore that
1813                 * means occasionally we think we have it but we dont
1814                 * This do-while loop checks for that case - this
1815                 * is better than putting a wait in the take semaphore
1816                 * rechecking loop fixes Mozdev bug 20088 */
1817                do
1818                {
1819 	           takeSwallowMutex(&mutex);
1820                }
1821                while(!stillHaveMutex(&mutex));
1822 #endif
1823                XSelectInput(dpy, parentDetails.window, SubstructureRedirectMask);
1824 	       XSelectInput(dpy, wattr.root, SubstructureNotifyMask);
1825 	       XSync(dpy, False);
1826 	  }
1827 
1828           pid = spawn_app(command, flags);
1829 	  if(pid == -1)
1830           {
1831                terminate(&mutex, -1, dpy);
1832 	       exit(EX_UNAVAILABLE);
1833           }
1834           sig_globals.childPid = pid;
1835 
1836 	  D("Waiting for pid=%d\n", pid);
1837 
1838 	  handle_app(dpy, &mutex);
1839 
1840 	  D("Wait done (repeats=%d, loops=%d)\n", repeats, loops);
1841 	  if (repeats < INF_LOOPS)
1842           {
1843 	       repeats -= loops;
1844 	  }
1845      }
1846 
1847      D("All done\n");
1848      terminate(&mutex, -1, dpy);
1849      return EXIT_SUCCESS;
1850 }
1851