예제 #1
0
def installCoreDependencies():
    requirements = getCoreDependencies()
    missing = checkRequirements(requirements, u"Lunchinator", u"Lunchinator")
    result = handleMissingDependencies(missing, optionalCallback=lambda req: not "yapsy" in req.lower())
    if result == INSTALL_CANCEL:
        return False

    try:
        import yapsy

        if lunchinator_has_gui():
            from PyQt4.QtGui import QMessageBox

            if result == INSTALL_SUCCESS:
                QMessageBox.information(
                    None,
                    "Success",
                    "Dependencies were installed successfully.",
                    buttons=QMessageBox.Ok,
                    defaultButton=QMessageBox.Ok,
                )
            elif result == INSTALL_FAIL:
                QMessageBox.warning(
                    None,
                    "Errors during installation",
                    "There were errors during installation, but Lunchinator might work anyways. If you experience problems with some plugins, try to install the required libraries manually using pip.",
                )
            return True
        getCoreLogger().info("yapsy is working after dependency installation")
        # without gui there are enough messages on the screen already
    except:
        if lunchinator_has_gui():
            try:
                from PyQt4.QtGui import QMessageBox

                QMessageBox.critical(
                    None,
                    "Error installing dependencies",
                    "There was an error, the dependencies could not be installed. Continuing without plugins.",
                )
            except:
                getCoreLogger().error(
                    "There was an error, the dependencies could not be installed. Continuing without plugins."
                )
        getCoreLogger().error(
            "Lunchinator is running without plugins because of missing dependencies. \
                Try executing 'lunchinator --install-dependencies' to install them automatically."
        )
        return False
예제 #2
0
 def activate(self):
     iface_gui_plugin.activate(self)
     if lunchinator_has_gui():
         self._sendMessageAction = _SendMessageAction()
         self._openChatAction = _OpenChatAction(self._sendMessageAction)
         self._peerActions = [self._openChatAction, _BlockAction(self._sendMessageAction), self._sendMessageAction]
     else:
         self._peerActions = None
예제 #3
0
def handleMissingDependencies(missing, optionalCallback=lambda _req : True):
    """If there are missing dependencies, asks and installs them.
    
    Returns a list of components whose requirements were not fully
    installed.
    
    missing -- dictionary returned by checkRequirements(...)
    optionalCallbacl -- function that takes a requirement string and returns
                        True if the requirement is optional and False
                        otherwise.
    """
    if missing:
        if isPyinstallerBuild():
            canInstall = False
            text = u"There are missing dependencies in your PyInstaller build. " +\
                u"Unfortunately, you cannot install additional packages for a PyInstaller build."
            getCoreLogger().warning(text + u"\n The missing dependencies are: \n" + unicode(str(missing)))
        else:
            canInstall = True
            text = None
        
        if lunchinator_has_gui():
            from lunchinator.req_error_dialog import RequirementsErrorDialog
            from PyQt4.QtGui import QMessageBox
            requirements = []
            for _component, missingList in missing.iteritems():
                for dispName, req, reason, info in missingList:
                    if reason == REASON_PACKAGE_MISSING:
                        reasonStr = u"Not installed"
                    elif reason == REASON_VERSION_CONFLICT:
                        reasonStr = u"Wrong version (installed: %s)" % info.version
                    else:
                        reasonStr = u"Unknown"
                    requirements.append((req,
                                         dispName,
                                         reasonStr,
                                         optionalCallback(req)))
            f = RequirementsErrorDialog(requirements, None, canInstall, text)
            res = f.exec_()
            if res == RequirementsErrorDialog.Accepted:
                if not canInstall:
                    return INSTALL_NOT_POSSIBLE
                installRes = installDependencies(f.getSelectedRequirements())
                if installRes == INSTALL_FAIL:
                    QMessageBox.critical(None, "Install Failed", "Some dependencies could not be installed.")
                elif installRes == INSTALL_SUCCESS:
                    QMessageBox.information(None, "Install Succeeded", "Dependencies were successfully installed.")
                elif installRes == INSTALL_RESTART:
                    QMessageBox.information(None, "Install Finished", "Lunchinator needs to be restarted to complete the installation.")
                return installRes
            elif res == RequirementsErrorDialog.IGNORED:
                return INSTALL_IGNORE
            else:
                return INSTALL_CANCEL
        return INSTALL_FAIL
    return INSTALL_NOT_POSSIBLE
예제 #4
0
def displayNotification(name, msg, logger, icon=None):
    if msg == None:
        msg = u""
    myPlatform = getPlatform()
    try: 
        from lunchinator import get_server
        if not lunchinator_has_gui():
            print time.strftime("%Y-%m-%d %H:%M"),name, msg
    except:
        print time.strftime("%Y-%m-%d %H:%M"),name, msg
    
    try:
        if myPlatform == PLATFORM_LINUX:
            fileToClose = None
            if icon is None or not os.path.exists(icon):
                icon = ""
            elif _mustScaleNotificationIcon():
                import Image
                im = Image.open(icon)
                im.thumbnail((64,64), Image.ANTIALIAS)
                fileToClose = NamedTemporaryFile(suffix='.png', delete=True)
                im.save(fileToClose, "PNG")
                fileToClose.flush()
                icon = fileToClose.name
            subprocess.call(["notify-send","--icon="+icon, name, msg])
            if fileToClose is not None:
                fileToClose.close()
        elif myPlatform == PLATFORM_MAC:
            fh = open(os.path.devnull,"w")
            exe = getBinary("terminal-notifier", os.path.join("bin", "terminal-notifier.app", "Contents", "MacOS"))
            if not exe:
                logger.warning("terminal-notifier not found.")
                return
            
            call = [exe, "-title", "Lunchinator: %s" % name, "-message", msg]
            if False and checkBundleIdentifier(_LUNCHINATOR_BUNDLE_IDENTIFIER): # no sender until code signing is fixed (probably never)
                call.extend(["-sender", _LUNCHINATOR_BUNDLE_IDENTIFIER])
                
            logger.debug(call)
            try:
                subprocess.call(call, stdout=fh, stderr=fh)
            except OSError as e:
                if e.errno == errno.EINVAL:
                    logger.warning("Ignoring invalid value on Mac")
                else:
                    raise
            except:
                logger.exception("Error calling %s", call)
        elif myPlatform == PLATFORM_WINDOWS:
            from lunchinator import get_server
            if hasattr(get_server().controller, "statusicon"):
                get_server().controller.statusicon.showMessage(name,msg)
    except:
        logger.exception("error displaying notification")
예제 #5
0
 def activate(self):
     iface_general_plugin.activate(self)
     
     try:
         from PyQt4.QtCore import QTimer
         from online_update.appupdate.git_update import GitUpdateHandler
         from online_update.appupdate.mac_update import MacUpdateHandler
         from online_update.appupdate.external_update import ExternalUpdateHandler
         from online_update.appupdate.win_update import WinUpdateHandler
         from online_update.appupdate.app_update_handler import AppUpdateHandler
         from online_update.repoupdate.repo_update_handler import RepoUpdateHandler
         self._activated = True
     except ImportError:
         self._activated = False
         self.logger.warning("ImportError, cannot activate Auto Update")
         return
     
     if GitUpdateHandler.appliesToConfiguration(self.logger):
         self._appUpdateHandler = GitUpdateHandler(self.logger)
     elif MacUpdateHandler.appliesToConfiguration(self.logger):
         self._appUpdateHandler = MacUpdateHandler(self.logger, self.hidden_options["check_url"])
     elif ExternalUpdateHandler.appliesToConfiguration(self.logger):
         self._appUpdateHandler = ExternalUpdateHandler(self.logger)
     elif WinUpdateHandler.appliesToConfiguration(self.logger):
         self._appUpdateHandler = WinUpdateHandler(self.logger, self.hidden_options["check_url"])
     else:
         self._appUpdateHandler = AppUpdateHandler(self.logger)
         
     self._repoUpdateHandler = RepoUpdateHandler(self.logger)
         
     self._appUpdateHandler.activate()
     self._repoUpdateHandler.activate()
     
     get_notification_center().connectInstallUpdates(self.installUpdates)
     get_notification_center().connectRepositoriesChanged(self._repoUpdateHandler.checkForUpdates)
         
     if lunchinator_has_gui():
         self._scheduleTimer = QTimer(getValidQtParent())
         self._scheduleTimer.timeout.connect(self.checkForUpdate)
         self._scheduleTimer.start(online_update.CHECK_INTERVAL)
예제 #6
0
 def _handle_core_event(self, ip, xmsg, newPeer, _fromQueue):
     ''' handles cmds that are not necessary for peer discovery but should work without plugins 
     '''
     
     # I don't see any reason to process these events for unknown peers.
     if newPeer:
         return
     cmd = xmsg.getCommand()
     value = xmsg.getCommandPayload()
     
     if cmd == "AVATAR":
         # someone wants to send me his pic via TCP
         values = value.split()
         file_size = int(values[0].strip())
         tcp_port = 0  # 0 means we must guess the port
         if len(values) > 1:
             tcp_port = int(values[1].strip())
         file_name = ""
         info = self._peers.getPeerInfo(pIP=ip)
         if u"avatar" in info:
             file_name = os.path.join(get_settings().get_avatar_dir(), info[u"avatar"])
         else:
             getCoreLogger().error("%s tried to send his avatar, but I don't know where to save it", ip)
         
         if len(file_name):
             pID = self._peers.getPeerID(pIP=ip)
             getCoreLogger().info("Receiving avatar from peer with ID %s, IP %s", pID, ip)
             self.controller.receiveFile(ip,
                                         file_size,
                                         file_name,
                                         tcp_port,
                                         successFunc=partial(get_notification_center().emitAvatarChanged,
                                                             pID,
                                                             info[u"avatar"]))
         
     elif cmd == "REQUEST_AVATAR":
         # someone wants my pic 
         other_tcp_port = get_settings().get_tcp_port()
         
         try:                    
             other_tcp_port = int(value.strip())
         except:
             getCoreLogger().exception("%s requested avatar, I could not parse the port from value %s, using standard %d", str(ip), str(value), other_tcp_port)
             
         fileToSend = os.path.join(get_settings().get_avatar_dir(), get_settings().get_avatar_file())
         if os.path.exists(fileToSend):
             fileSize = os.path.getsize(fileToSend)
             getCoreLogger().info("Sending file of size %d to %s : %d", fileSize, str(ip), other_tcp_port)
             self.call("HELO_AVATAR %s %s" % (fileSize, other_tcp_port), peerIPs = [ip])
             self.controller.sendFile(ip, fileToSend, other_tcp_port)
         else:
             # TODO should this be an error? If somebody deletes the avatar file, it should be reset silently -> warning
             getCoreLogger().error("Want to send file %s, but cannot find it", fileToSend)   
         
     elif cmd == "REQUEST_LOGFILE":
         # someone wants my logfile 
         other_tcp_port = get_settings().get_tcp_port()
         try:                
             (oport, _) = value.split(" ", 1)    
             other_tcp_port = int(oport.strip())
         except:
             getCoreLogger().warning("%s requested the logfile, I could not parse the port and number from value %s, using standard %d and logfile 0", str(ip), str(value), other_tcp_port)
         
         fileToSend = StringIO()
         with contextlib.closing(tarfile.open(mode='w:gz', fileobj=fileToSend)) as tarWriter:
             if os.path.exists(get_settings().log_file()):
                 tarWriter.add(get_settings().log_file(), arcname="0.log")
             logIndex = 1
             while os.path.exists("%s.%d" % (get_settings().log_file(), logIndex)):
                 tarWriter.add("%s.%d" % (get_settings().log_file(), logIndex), arcname="%d.log" % logIndex)
                 logIndex = logIndex + 1
         
         fileSize = fileToSend.tell()
         getCoreLogger().info("Sending file of size %d to %s : %d", fileSize, str(ip), other_tcp_port)
         self.call("HELO_LOGFILE_TGZ %d %d" % (fileSize, other_tcp_port), peerIPs=[ip])
         self.controller.sendFile(ip, fileToSend.getvalue(), other_tcp_port, True)      
         
     elif cmd == "PIPE":
         #to hell, I am going to print this in the hope that this is ASCII art
         if not lunchinator_has_gui():
             print value
예제 #7
0
    def perform_call(self, msg, peerIDs, peerIPs):
        """Only the controller should invoke this method -> Called from main thread
        both peerIDs and peerIPs should be sets
        Used also by start_lunchinator to send messages without initializing
        the whole lunch server."""     
        msg = convert_string(msg) # make sure, msg is unicode
        target = []
        
        if len(peerIDs) == 0 and len(peerIPs) == 0:
            target = self._peers.getFirstPeerIP()
        else:
            target = peerIPs
            for pID in peerIDs:
                pIPs = self._peers.getFirstPeerIP(pID=pID)
                if len(pIPs):
                    target = target.union(pIPs)
                else:
                    getCoreLogger().warning("While calling: I do not know a peer with ID %s, ignoring ", pID)
    
        if 0 == len(target):            
            getCoreLogger().warning("Cannot send message (%s), there is no peer given or none found", msg)
            
        if lunchinator_has_gui() and \
           get_settings().get_warn_if_members_not_ready() and \
           not msg.startswith(u"HELO") and \
           get_settings().get_lunch_trigger().upper() in msg.upper():
            # check if everyone is ready
            notReadyMembers = [self._peers.getDisplayedPeerName(pID=peerID) for peerID in peerIDs if not self._peers.isPeerReady(pID=peerID)]
            
            if notReadyMembers:
                    
                if len(notReadyMembers) == 1:
                    warn = "%s is not ready for lunch." % iter(notReadyMembers).next()
                elif len(notReadyMembers) == 2:
                    it = iter(notReadyMembers)
                    warn = "%s and %s are not ready for lunch." % (it.next(), it.next())
                else:
                    warn = "%s and %d others are not ready for lunch." % (random.sample(notReadyMembers, 1)[0], len(notReadyMembers) - 1)
                try:
                    from PyQt4.QtGui import QMessageBox
                    warn = "WARNING: %s Send lunch call anyways?" % warn
                    result = QMessageBox.warning(None,
                                                 "Members not ready",
                                                 warn,
                                                 QMessageBox.Yes | QMessageBox.No,
                                                 QMessageBox.No)
                    if result == QMessageBox.No:
                        return
                except ImportError:
                    print warn

        i = 0
        s = lunchSocket(self._peers)
        try:                  
            self._send_logger.info(msg)
            for ip in target:
                try:
                    short = msg if len(msg)<15 else msg[:14]+"..."
                    self._send_logger.debug("To %s: %s", ip.strip(), short)
                    s.sendto(msg, ip.strip())
                    i += 1                    
                except socket.error as e:
                    if e.errno in [64,65]:
                        getCoreLogger().debug("lunch_socket: Removing IP because host is down or there is no route")
                        self._peers.removePeerIPs([ip])
                    else:
                        getCoreLogger().warning("The following message could not be delivered to %s: %s", ip, msg, exc_info=1)
                except Exception as e:
                    getCoreLogger().warning("The following message could not be delivered to %s: %s", ip, msg, exc_info=1)
        finally:
            s.close() 
        return i
예제 #8
0
    def start_server(self):
        '''listening method - should be started in its own thread''' 
        
        getCoreLogger().info("%s - Starting the lunch notifier service", strftime("%a, %d %b %Y %H:%M:%S", localtime()).decode("utf-8"))
        
        self.my_master = -1  # the peer i use as master
        
        is_in_broadcast_mode = False
        
        self._recv_socket = lunchSocket(self._peers)
        try: 
            self._recv_socket.bind()
            self.running = True
            self._cleanupLock = loggingMutex("cleanup", logging=get_settings().get_verbose())
            self._startCleanupTimer()
            self.controller.initDone()
            
            #first thing to do: ask stored peers for their info:
            if len(self._peers) == 0:
                requests = self._peers.initPeersFromFile()
                self.call_request_info(requests)
            
            while self.running:
                try:
                    xmsg, ip = self._recv_socket.recv()
                    try:
                        plainMsg = xmsg.getPlainMessage()                        
                        self._recv_logger.info("From %s: %s",ip,plainMsg)
                    except:
                        getCoreLogger().exception("There was an error when trying to parse a message from %s", ip)
                        continue
                     
                    # check for local address: only stop command allowed, else ignore
                    if ip.startswith("127."):
                        if xmsg.getCommand() == "STOP":
                            getCoreLogger().info("Got Stop Command from localhost: %s", plainMsg)
                            self.running = False
                            self.exitCode = EXIT_CODE_STOP
                        elif xmsg.getCommand() == "OPEN_WINDOW" and lunchinator_has_gui():
                            self.controller._openWindow.emit()
                        elif xmsg.getCommand() == "LOCAL_PIPE":
                            getCoreLogger().debug("Relaying LOCAL_PIPE call")
                            self.call_all_members("HELO_PIPE "+xmsg.getCommandPayload())
                        continue
                    
                    # first we save the timestamp of this contact, no matter what
                    self._peers.seenIP(ip)

                    # check if we know this peer          
                    isNewPeer = self._peers.getPeerInfo(pIP=ip) == None          
                    if isNewPeer and self._should_call_info_on_event(plainMsg):
                        #this is a new member - we ask for info right away
                        self.call_request_info([ip])
                    
                    self._handle_event(xmsg, ip, time(), isNewPeer, False)
                except splitCall as e:
                    getCoreLogger().debug(e.value)
                except socket.timeout:                    
                    if len(self._peers) > 1:                     
                        if is_in_broadcast_mode:
                            is_in_broadcast_mode = False
                            getCoreLogger().info("ending broadcast")       
                    else:
                        if not self._disable_broadcast:
                            if not is_in_broadcast_mode:
                                is_in_broadcast_mode = True
                                getCoreLogger().info("seems like you are alone - broadcasting for others")
                            s_broad = lunchSocket(self._peers)
                            msg = 'HELO_REQUEST_INFO ' + self._build_info_string()
                            self._send_logger.info(msg)
                            s_broad.broadcast(msg)
                            s_broad.close()
                            #forgotten peers may be on file
                            requests = self._peers.initPeersFromFile()
                            self.call_request_info(requests)
                            
        except socket.error as e:
            # socket error messages may contain special characters, which leads to crashes on old python versions
            getCoreLogger().error(u"stopping lunchinator because of socket error: %s", convert_string(str(e)))
        except KeyboardInterrupt:
            getCoreLogger().info("Received keyboard interrupt, stopping.")
        except:
            getCoreLogger().exception("stopping - Critical error: %s", str(sys.exc_info())) 
        finally: 
            self.running = False
            try:
                #make sure to close the cleanup thread first
                with self._cleanupLock:
                    self._cleanupTimer.cancel()
                
                self.call("HELO_LEAVE bye")
                self._recv_socket.close()
                self._recv_socket = None 
            except:
                getCoreLogger().warning("Wasn't able to send the leave call and close the socket...")
            self._finish()
예제 #9
0
 def appliesToConfiguration(cls, _logger):
     return lunchinator_has_gui() and getPlatform() == PLATFORM_MAC and getApplicationBundle() != None
예제 #10
0
 def get_peer_actions(self):
     if lunchinator_has_gui():
         self._rpAction = _RemotePictureAction()
         return [self._rpAction]
     else:
         return None
예제 #11
0
 def extendsInfoDict(self):
     return lunchinator_has_gui()
예제 #12
0
 def process_command(self, xmsg, ip, peer_info, preprocessedData=None):
     if xmsg.getCommand()=="PIPE":
         data = xmsg.getCommandPayload()
         if lunchinator_has_gui():
             self._outputField.setText(data)
             self._outputField.setStatusTip("sent by "+peer_info[u"name"])
예제 #13
0
 def extendsInfoDict(self):
     # do not except file transfers without GUI
     return lunchinator_has_gui()
예제 #14
0
 def appliesToConfiguration(cls, _logger):
     return lunchinator_has_gui() and getPlatform() == PLATFORM_WINDOWS
예제 #15
0
 def canCheckForUpdate(self):
     return lunchinator_has_gui()
예제 #16
0
 def has_gui(self):
     """ returns if a GUI and qt is present
     @deprecated: use lunchinator.lunchinator_has_gui() instead
     """
     return lunchinator_has_gui()
예제 #17
0
    def _alertIfIPnotMyself(self, newPID, peerInfo):
        """ alert if ID is mine but ip is not from my machine
        
        this function has to be called from the main thread
        
        @return: True if that's my ID from another machine
        
        @type newPID: unicode
        @type peerInfo: dict
        @rtype: bool
        """

        if not peerInfo.has_key("triggerIP") or newPID != get_settings().get_ID():
            # that's not me!
            return False

        ip = peerInfo["triggerIP"]
        myname = socket.gethostname()  # socket.getfqdn(socket.gethostname())
        othername = ""
        try:
            othername = socket.gethostbyaddr(ip)[0]
        except:
            self.logger.warning(
                "Another IP (%s) contacted me with my ID, I can't find it's hostname..., won't do anything now." % ip
            )
            return False

        # make sure, we only check the hostname, not the fqdn
        i = myname.find(".")
        if i != -1:
            myname = myname[:i]
        i = othername.find(".")
        if i != -1:
            othername = othername[:i]

        if myname == othername:
            # that seems to be me from another, maybe on a second
            # network interface
            return False

        if othername in get_settings().get_multiple_machines_allowed():
            # he is allowed to do that
            return False

        # that seems to be coming from an unknown machine and has to be reported
        from lunchinator import lunchinator_has_gui

        msg = (
            "Another lunchinator on the network (%s: %s)" % (ip, othername)
            + "is identifying itself with your (%s) ID. " % myname
            + "It will get all messages you get, also private ones!\n"
        )

        if lunchinator_has_gui():
            msg += "If this is not what you want, you should create a new ID immediately."
            from PyQt4.QtGui import QMessageBox, QPushButton

            msgBox = QMessageBox(None)
            #             msgBox.setIcon(QMessageBox.Warning)
            #             msgBox.setWindowTitle("Another Lunchinator with your ID detected")
            msgBox.setText(msg)
            msgBox.addButton(QPushButton("Create New ID"), QMessageBox.AcceptRole)
            msgBox.addButton(QPushButton("Ignore"), QMessageBox.NoRole)
            msgBox.addButton(QPushButton("Allow host to get my messages"), QMessageBox.RejectRole)
            ret = msgBox.exec_()
            if ret == QMessageBox.AcceptRole:
                get_settings().generate_ID()
            elif ret != QMessageBox.NoRole:
                get_settings().add_multiple_machines_allowed(othername)
        else:
            msg += (
                "If you are sure that this is right you can set "
                + "multiple_machines_allowed = %s in your settings.cfg \n" % ip
                + "Otherwise you should create a new ID immediately.\n"
            )
            self.logger.critical(msg)
        return True
예제 #18
0
 def activate(self):
     get_notification_center().connectOutdatedRepositoriesChanged(self._processOutdated)
     if lunchinator_has_gui():
         self.checkForUpdates()