ó
Gh\c           @   s<  d  d l  m Z m Z d  d l m Z m Z d  d l Z d  d l Z d  d l m	 Z	 d  d l
 Z
 d  d l Z d  d l m Z m Z m Z d  d l m Z d  d l m Z d  d l m Z d  d	 l m Z d
 Z i  Z e ƒ  Z y d  d l Z Wn n Xd „  Z d e j f d „  ƒ  YZ d e f d „  ƒ  YZ d „  Z d S(   iÿÿÿÿ(   t   Popent   PIPE(   t   Eventt   LockN(   t   exc_info(   t   mbnamest   CustomConfigt   OfflineImapError(   t   globals(   t
   Repository(   t   getglobalui(   t   InstanceLimitedThreadt   LIMITED_FOLDER_c         C   s&   g  |  j  d ƒ D] } | j ƒ  ^ q S(   Nt   Account(   t   getsectionlistt   lstrip(   t   customconfigt   name(    (    s.   /usr/share/offlineimap/offlineimap/accounts.pyt   getaccountlist*   s    R   c           B   s’   e  Z d  Z e ƒ  Z e ƒ  Z d „  Z d „  Z d „  Z d „  Z	 d „  Z
 d „  Z d „  Z e d „  ƒ Z d	 „  Z d
 „  Z d „  Z d „  Z RS(   s×   Represents an account (ie. 2 repositories) to sync.

    Most of the time you will actually want to use the derived
    :class:`accounts.SyncableAccount` which contains all functions used
    for syncing an account.c         C   sð   | |  _  | |  _ | j ƒ  |  _ | j ƒ  |  _ |  j d t ƒ |  _ t	 ƒ  |  _
 |  j d d ƒ |  _ |  j  j d d ƒ |  _ d |  _ |  j d k  r¶ |  j
 j d | ƒ d |  _ n  |  j d k rÑ d |  _ n  d |  _ d |  _ d |  _ d S(	   s  
        :param config: Representing the offlineimap configuration file.
        :type config: :class:`offlineimap.CustomConfig.CustomConfigParser`

        :param name: A (str) string denoting the name of the Account
                     as configured.
        t   utf8foldernamest   autorefreshg        t   generals   dry-runi    s/   autorefresh for %s is negative, fixing it to 0.N(   t   configR   t   getmetadatadirt   metadatadirt   getlocalevalt	   localevalt   getconfbooleant   Falset   utf_8_supportR
   t   uit   getconffloatt   refreshperiodt
   getbooleant   dryrunt   quicknumt   warnt   Nonet   remoterepost
   localrepost   statusrepos(   t   selfR   R   (    (    s.   /usr/share/offlineimap/offlineimap/accounts.pyt   __init__;   s$    						c         C   s   |  j  S(   N(   R   (   R)   (    (    s.   /usr/share/offlineimap/offlineimap/accounts.pyR   Y   s    c         C   s   |  j  S(   N(   R   (   R)   (    (    s.   /usr/share/offlineimap/offlineimap/accounts.pyt	   getconfig]   s    c         C   s   |  j  S(   N(   R   (   R)   (    (    s.   /usr/share/offlineimap/offlineimap/accounts.pyt   getname`   s    c         C   s   |  j  S(   N(   R   (   R)   (    (    s.   /usr/share/offlineimap/offlineimap/accounts.pyt   __str__c   s    c         C   s   t  j j |  j d |  j ƒ S(   Ns   Account-(   t   ost   patht   joinR   R   (   R)   (    (    s.   /usr/share/offlineimap/offlineimap/accounts.pyt   getaccountmetaf   s    c         C   s   d |  j  ƒ  S(   Ns   Account (   R,   (   R)   (    (    s.   /usr/share/offlineimap/offlineimap/accounts.pyt
   getsectionj   s    c         C   sy   | d k r= xf t  | ƒ D] } | j d | d d ƒ q Wn8 | d k rY |  j j ƒ  n | d k ru |  j j ƒ  n  d S(   sY  Set skip sleep/abort event for all accounts.

        If we want to skip a current (or the next) sleep, or if we want
        to abort an autorefresh loop, the main thread can use
        set_abort_event() to send the corresponding signal. Signum = 1
        implies that we want all accounts to abort or skip the current
        or next sleep phase. Signum = 2 will end the autorefresh loop,
        ie all accounts will return after they finished a sync. signum=3
        means, abort NOW, e.g. on SIGINT or SIGTERM.

        This is a class method, it will send the signal to all accounts.
        i   s   Account t	   skipsleept   1i   i   N(   R   t   sett   abort_soon_signalt   abort_NOW_signal(   t   clsR   t   signumt   acctsection(    (    s.   /usr/share/offlineimap/offlineimap/accounts.pyt   set_abort_eventm   s    c         C   sY   |  j  d d ƒ } | r7 |  j j |  j ƒ  d d ƒ n  | pX t j j ƒ  pX t j j ƒ  S(   sv  Checks if an abort signal had been sent.

        If the 'skipsleep' config option for this account had been set,
        with `set_abort_event(config, 1)` it will get cleared in this
        function. Ie, we will only skip one sleep and not all.

        :returns: True, if the main thread had called
            :meth:`set_abort_event` earlier, otherwise 'False'.
        R3   i    t   0(   R   R   R5   R2   R   R6   t   is_setR7   (   R)   R3   (    (    s.   /usr/share/offlineimap/offlineimap/accounts.pyt   get_abort_event‡   s
    c         C   sî   |  j  s d Sg  } t |  d ƒ r5 | j |  j ƒ n  t |  d ƒ rW | j |  j ƒ n  x | D] } | j ƒ  q^ Wt |  j  d ƒ } |  j j | |  ƒ } x | D] } | j	 ƒ  q¡ W| rê t
 j j ƒ  sÙ t
 j j ƒ  rÝ d Sd |  _ d Sd S(   sÝ   Sleep if the account is set to autorefresh.

        :returns: 0:timeout expired, 1: canceled the timer,
                  2:request to abort the program,
                  100: if configured to not sleep at all.
        id   R'   R&   i<   i   i    i   (   R    t   hasattrt   appendR'   R&   t   startkeepalivet   intR   t   sleept   stopkeepaliveR   R6   R=   R7   R#   (   R)   t   kaobjst   itemR    t   sleepresult(    (    s.   /usr/share/offlineimap/offlineimap/accounts.pyt   _sleeper˜   s(    		c         C   sH   t  |  d ƒ } t  |  d ƒ } |  j j | d ƒ |  j j | d ƒ d S(   s1   Output diagnostics for all involved repositories.t   remotet   localt   Remotet   LocalN(   R	   R   t   serverdiagnostics(   R)   t   remote_repot
   local_repo(    (    s.   /usr/share/offlineimap/offlineimap/accounts.pyRM   ¼   s    c         C   s‰   t  |  d ƒ } yO |  j r8 |  j j d | |  f ƒ n! | j | ƒ |  j j d | ƒ d SWn$ t k
 r„ } |  j j | ƒ d SXd  S(   NRI   s2   would try to remove '%s' on remote of '%s' accounts   Folder '%s' deleted.i    i   (   R	   R"   R   t   infot   deletefoldert	   Exceptiont   error(   R)   t
   foldernameRN   t   e(    (    s.   /usr/share/offlineimap/offlineimap/accounts.pyRQ   Æ   s    	(   t   __name__t
   __module__t   __doc__R   R6   R7   R*   R   R+   R,   R-   R1   R2   t   classmethodR;   R>   RH   RM   RQ   (    (    (    s.   /usr/share/offlineimap/offlineimap/accounts.pyR   /   s   											$	
t   SyncableAccountc           B   sM   e  Z d  Z d „  Z d „  Z d „  Z d „  Z d „  Z d „  Z d „  Z	 RS(   s0  A syncable email account connecting 2 repositories.

    Derives from :class:`accounts.Account` but contains the additional
    functions :meth:`syncrunner`, :meth:`sync`, :meth:`syncfolders`,
    used for syncing.

    In multi-threaded mode, one instance of this object is run per "account"
    thread.c         O   sE   t  j |  | | Ž d  |  _ t j j |  j j ƒ  d |  ƒ |  _	 d  S(   Ns   %s.lock(
   R   R*   R%   t   _lockfdR.   R/   R0   R   R   t   _lockfilepath(   R)   t   argst   kwargs(    (    s.   /usr/share/offlineimap/offlineimap/accounts.pyR*   à   s    		c         C   s˜   t  |  j d ƒ |  _ y! t j |  j t j t j Bƒ Wn[ t k
 rI nK t k
 r“ |  j j	 ƒ  t
 j t t d |  t j j ƒ t ƒ  d ƒ n Xd S(   s@   Lock the account, throwing an exception if it is locked already.t   wsB   Could not lock account %s. Is another instance using this account?i   N(   t   openR\   R[   t   fcntlt   lockft   LOCK_EXt   LOCK_NBt	   NameErrort   IOErrort   closet   sixt   reraiseR   t   ERRORt   REPOR   (   R)   (    (    s.   /usr/share/offlineimap/offlineimap/accounts.pyt   __lockæ   s    !	c         C   sR   |  j  rN |  j  j rN |  j  j ƒ  y t j |  j ƒ WqN t k
 rJ qN Xn  d S(   s*   Unlock the account, deleting the lock fileN(   R[   t   closedRg   R.   t   unlinkR\   t   OSError(   R)   (    (    s.   /usr/share/offlineimap/offlineimap/accounts.pyt   _unlockø   s    c         C   s   |  j  j |  ƒ yk |  j ƒ  } t j j | ƒ sD t j | d ƒ n  t |  d ƒ |  _ t |  d ƒ |  _	 t |  d ƒ |  _
 WnI t k
 rÆ } |  j  j | t ƒ  d ƒ | j t j j k rÂ ‚  n  d SXd } xL| r|  j  j |  ƒ zó y |  j ƒ  |  j ƒ  WnÂ t t f k
 r‚  n» t k
 r} | j t j j k rr| rT| d 8} n  | j t j j k rr‚  qrn  |  j  j | t ƒ  d ƒ nI t k
 rÅ} |  j  j | t ƒ  d d	 d
 |  ƒn X|  j rØd } n  Wd |  j  j |  ƒ |  j ƒ  | r|  j ƒ  d k rd } n  XqÐ Wd S(   s4   The target for both single and multi-threaded modes.iÀ  RI   RJ   t   statusi   Ni   i   t   msgs%   While attempting to sync account '%s'i    (   R   t   registerthreadR1   R.   R/   t   existst   mkdirR	   R&   R'   R(   R   RS   R   t   severityRj   t   CRITICALt   acctt   _SyncableAccount__lockt   _SyncableAccount__synct   KeyboardInterruptt
   SystemExitRk   RR   R    t   acctdoneRp   RH   (   R)   t   accountmetadataRU   t   looping(    (    s.   /usr/share/offlineimap/offlineimap/accounts.pyt
   syncrunner  sL    	 
		
c         C   s4   |  j  j | j ƒ  j |  j j ƒ  |  j  j ƒ  ƒ ƒ S(   s?   Return the corresponding local folder for a given remotefolder.(   R'   t	   getfoldert   getvisiblenamet   replaceR&   t   getsep(   R)   t   remotefolder(    (    s.   /usr/share/offlineimap/offlineimap/accounts.pyt   get_local_folder3  s    	c         C   sÊ  g  } |  j  d d ƒ } |  j | ƒ |  j rs |  j j ƒ  rs t d d |  j ƒ  d d d d t j j ƒ ‚ n  |  j	 d	 d
 ƒ } | d
 k  rš t
 } n[ | d
 k rï |  j d
 k sÄ |  j | k rÖ d |  _ t } qõ |  j d |  _ t
 } n t } y}t } |  j } |  j } |  j } | j ƒ  | j ƒ  | j | | ƒ | j d t ƒ se|  j j | | ƒ n  x—| j ƒ  D]‰}	 t j j ƒ  r‹Pn  |	 j s½|  j j d d |	 j ƒ  | f ƒ qrn  | j ƒ  }
 |
 t j j k r.|
 | j ƒ  k r.|
 |	 j ƒ  k r.|  j j d d |	 j ƒ  | j ƒ  f ƒ qrn  |  j |	 ƒ } | j sr|  j j d d | j ƒ  | j f ƒ qrn  t  j! j" såt# d d t$ |  j j ƒ  f d t% d d |	 j& ƒ  |  f d |  |	 | f ƒ } | j' ƒ  | j( | ƒ n t% |  |	 | ƒ t
 } qrWx | D] } | j) ƒ  qW| t
 k r9t* j+ |  j, ƒ n$ d j- |  ƒ } t | t j j ƒ ‚ | j. ƒ  | j. ƒ  Wn | j/ ƒ  | j/ ƒ  ‚  n X| j0 ƒ  | j0 ƒ  |  j  d d ƒ } |  j | ƒ d S(   sì   Synchronize the account once, then return.

        Assumes that `self.remoterepos`, `self.localrepos`, and
        `self.statusrepos` has already been populated, so it should only
        be called from the :meth:`syncrunner` function.t   presynchookt    s"   Configuration mismatch in account s   '%s'. s2   
Account setting 'utf8foldernames' and repository s3   setting 'decodefoldernames'
may not be used at the s3   same time. This account has not been synchronized.
s1   Please check the configuration and documentation.t   quicki    i   t   readonlys$   Not syncing filtered folder '%s'[%s]sR   Ignoring folder '%s' due to unsupported '%s' character serving as local separator.t   limitNamespaces   %s%st   targetR   s   Folder %s [acc: %s]R]   s3   Account {}: no folder to sync (folderfilter issue?)t   postsynchookN(1   t   getconft   callhookR   R&   t   getdecodefoldernamesR   R,   Rj   Rk   t
   getconfintt   TrueR#   R   R'   R(   t
   getfolderst   sync_folder_structureR   R   t   syncfoldersR   R7   R=   t	   sync_thist   debugR„   R.   R/   t   sepR$   R†   t
   repositoryR   t   optionst   singlethreadingR   t   FOLDER_NAMESPACEt
   syncfoldert   getexplainednamet   startR@   R0   R   t   writeIntermediateFileR   t   formatt   forgetfolderst   dropconnectionst   holdordropconnections(   R)   t   folderthreadst   hookt   quickconfigR‰   t   startedThreadR&   R'   R(   R…   R˜   t   localfoldert   threadt   thrRr   (    (    s.   /usr/share/offlineimap/offlineimap/accounts.pyt   __sync=  s–    							

		






c         C   sö   t  j j ƒ  r d  S| s d  Sy† |  j j d | ƒ |  j rA d  St | d t d t d t d t d t ƒ} | j	 ƒ  } |  j j d | ƒ |  j j d | j
 ƒ WnL t t f k
 r¿ ‚  n3 t k
 rñ } |  j j | t ƒ  d	 d
 d ƒn Xd  S(   Ns   Calling hook: t   shellt   stdint   stdoutt   stderrt	   close_fdss   Hook stdout: %s
Hook stderr:%s
s   Hook return code: %di   Rr   s   Calling hook(   R   R7   R=   R   R   R"   R    R’   R   t   communicatet
   returncodeR{   R|   RR   RS   R   (   R)   t   cmdt   pt   rRU   (    (    s.   /usr/share/offlineimap/offlineimap/accounts.pyR   ²  s$    		(
   RV   RW   RX   R*   Ry   Rp   R€   R†   Rz   R   (    (    (    s.   /usr/share/offlineimap/offlineimap/accounts.pyRZ   Ö   s   				0	
	uc            sT  ‡  ‡ f d †  } ‡  ‡ f d †  } ‡ ‡ ‡ ‡ ‡ f d †  } ‡ ‡ f d †  } d „  } ˆ  j  } ˆ  j ‰ ˆ  j }	 t ƒ  ‰ ˆ j ˆ  ƒ zsyÀˆ  j ˆ ƒ ‰ | ƒ  t j ˆ  j ˆ j	 ƒ  ˆ j
 ƒ  ƒ |	 j ˆ j ƒ  j | j ƒ  |	 j ƒ  ƒ ƒ ‰ ˆ j ƒ  ˆ j ƒ  ˆ j | ˆ ˆ ˆ ƒ ˆ j ƒ  }
 ˆ j ƒ  } ˆ j ƒ  } |
 d
 k | d
 k | d
 k d k r–t j t t d t j j ƒ t ƒ  d ƒ n  |
 d
 k s®| s®| rÄ| rÄˆ j d	 ƒ n  |
 d
 k rä| |
 ƒ | ƒ  n« | d
 k r
| ˆ ˆ | ƒ | ƒ  n… | d
 k r0| ˆ ˆ | ƒ | ƒ  n_ ˆ j ƒ  | r~ˆ j ˆ ƒ r~ˆ j ˆ ƒ r~ˆ j ˆ ƒ ˆ j ƒ  d
 Sn  | ƒ  ˆ j ƒ  ˆ j d t  ƒ sÊˆ j! | ˆ ˆ ˆ ƒ ˆ j" ˆ ˆ ƒ n ˆ j# d d ˆ j
 ƒ  ƒ | j d t  ƒ sˆ j! ˆ ˆ | ˆ ƒ ˆ j" ˆ ˆ ƒ n ˆ j# d d | j
 ƒ  ƒ ˆ j$ ƒ  ˆ j ƒ  Wn¬ t% t& f k
 rj‚  n“ t k
 r¾} | j' t j j( k r”‚  qýˆ j) | t ƒ  d d d ˆ ˆ  f ƒn? t* k
 rü} ˆ j) | d d ˆ  ˆ j ƒ  t+ j, ƒ  f ƒn XWd
 x: d d d g D]) } | t- ƒ  k rt- ƒ  | j. ƒ  qqWˆ j/ ƒ  | ƒ  Xd
 S(   sØ   Synchronizes given remote folder for the specified account.

    Filtered folders on the remote side will not invoke this function.

    When called in concurrently for the same localfolder, syncs are
    serialized.c             sŠ   ˆ  j  ƒ  }  ˆ j ƒ  } t T t j |  ƒ d  k rA i  t |  <n  t |  j | ƒ d  k rn t ƒ  t |  | <n  Wd  QXt |  | j ƒ  d  S(   N(   R,   t   getfullnamet   SYNC_MUTEXES_LOCKt   SYNC_MUTEXESt   getR%   R   t   acquire(   t   account_namet   localfolder_name(   t   accountR©   (    s.   /usr/share/offlineimap/offlineimap/accounts.pyt   acquire_mutexÒ  s    c              s"   t  ˆ  j ƒ  ˆ j ƒ  j ƒ  d  S(   N(   R¹   R,   R·   t   release(    (   R¾   R©   (    s.   /usr/share/offlineimap/offlineimap/accounts.pyt   release_mutexã  s    c              s   ˆ  j  ƒ  d k s$ ˆ j  ƒ  d k rx ˆ  j ƒ  sN ˆ j ˆ  ƒ ˆ  j j ƒ  d  Sˆ j ƒ  sŒ ˆ j ˆ ƒ ˆ j ƒ  d  Sn ˆ  j ƒ  ˆ j ƒ  d  S(   Ni    (   t   getmessagecountt   check_uidvalidityt   validityproblemR™   t   restore_atimet   save_uidvalidity(    (   R©   R'   R…   t   statusfolderR   (    s.   /usr/share/offlineimap/offlineimap/accounts.pyt   check_uid_validityæ  s    $

c            s  ˆ j  d t j t j |  ƒ d ƒ ƒ ˆ j ƒ  } ˆ  j ƒ  t | ƒ d k r‡ ˆ j ƒ  ˆ j  d t | ƒ ƒ ˆ  j  d t | ƒ ƒ nv ˆ  j  d |  ƒ ˆ  j ƒ  } g  | D] } | d k rª | ^ qª } t | ƒ d k rý ˆ j ƒ  ˆ j  d t | ƒ ƒ n  d S(	   sB   Returns messages with uid > min(uids of messages newer than date).t   min_datei   i<   i    t   min_uidNi   i€Q (   t   cachemessagelistt   timet   gmtimet   mktimet   getmessageuidlistt   dropmessagelistcachet   lent   min(   t   datet   uidst   uid(   R©   R…   (    s.   /usr/share/offlineimap/offlineimap/accounts.pyt   cachemessagelists_upto_dateú  s    	

%
c         S   s÷   |  j  ƒ  | j ƒ  } | d k rã t |  j ƒ  ƒ d k rh t d | j j |  j j f t j j	 ƒ ‚ qó | j  d | ƒ g  t
 | j j ƒ  ƒ D] } | d k rŽ | ^ qŽ } t | ƒ d k rÍ t | ƒ } n d } | j | ƒ n | j  d | ƒ d S(   s»  Retrieve messagelists when startdate has been set for
        the folder 'partial'.

        Idea: suppose you want to clone the messages after date in one
        account (partial) to a new one (new). If new is empty, then copy
        messages in partial newer than date to new, and keep track of the
        min uid. On subsequent syncs, sync all the messages in new against
        those after that min uid in partial. This is a partial replacement
        for maxage in the IMAP-IMAP sync case, where maxage doesn't work:
        the UIDs of the messages in localfolder might not be in the same
        order as those of corresponding messages in remotefolder, so if L in
        local corresponds to R in remote, the ranges [L, ...] and [R, ...]
        might not correspond. But, if we're cloning a folder into a new one,
        [min_uid, ...] does correspond to [1, ...].

        This is just for IMAP-IMAP. For Maildir-IMAP, use maxage instead.i    s>   To use startdate on Repository %s, Repository %s must be emptyRÉ   i   RÊ   N(   RË   t   retrieve_min_uidR%   RÑ   RÏ   R   R™   R   Rj   t   MESSAGEt   listt   messagelistt   keysRÒ   t   save_min_uid(   t   newt   partialRÓ   RÊ   RÕ   t   positive_uids(    (    s.   /usr/share/offlineimap/offlineimap/accounts.pyt   cachemessagelists_startdate  s    
4i   su   You can set at most one of the following: maxage, startdate (for the local folder), startdate (for the remote folder)i   sT   Quick syncs (-q) not supported in conjunction with maxage or startdate; ignoring -q.NRŠ   Rˆ   s(   Not syncing to read-only repository '%s'Rr   s&   Aborting sync, folder '%s' [acc: '%s']s(   ERROR in syncfolder for %s folder %s: %sRÇ   R©   R…   (0   R&   R'   R(   R
   Rs   R†   R   t   addR   t   getlocalrootR,   R   R‚   Rƒ   R„   t	   openfilesRË   t   syncingfoldert	   getmaxaget   getstartdateR%   Rh   Ri   R   Rj   Rk   R   R$   t   quickchangedt   skippingfolderRÅ   R   R   t   syncingmessagest   syncmessagestoR—   t   saveR{   R|   Rv   t   FOLDERRS   RR   t	   tracebackt
   format_exct   localsRÐ   t
   closefiles(   R¾   R…   R‰   R¿   RÁ   RÈ   RÖ   Rà   R&   R(   t   maxaget
   localstartt   remotestartRU   t   folder(    (   R¾   R©   R'   R…   RÇ   R   s.   /usr/share/offlineimap/offlineimap/accounts.pyR   Ê  sœ    	)				 

&	

	
	




%
(    t
   subprocessR    R   t	   threadingR   R   R.   RÌ   t   sysR   Rí   Rh   t   offlineimapR   R   R   R   t   offlineimap.repositoryR	   t   offlineimap.uiR
   t   offlineimap.threadutilR   Rœ   R¹   R¸   Ra   R   t   ConfigHelperMixinR   RZ   R   (    (    (    s.   /usr/share/offlineimap/offlineimap/accounts.pyt   <module>   s,   		§ô