
    eC_                         d dl Z d dlZd dlZd dlZd dlZd dlmZ d dlmZ d dl	m
Z
 d dlmZ ddlmZ d dlmZ  ej"                  d	      Z ej"                  d
      Zi a e       addZ G d de      Zy)    N)exc_info)Lock)md5)OfflineImapError   )
BaseFolder)NoBoundaryInMultipartDefectz,U=(\d+)z(\d+)c                     t         j                          	 | t        t        j                               } | t        v rt        | xx   dz  cc<   n	dt        | <   | t        |    ft         j                          S # t         j                          w xY w)Nr   r   )timelockacquireinttimetimehashreleasedates    5/usr/share/offlineimap3/offlineimap/folder/Maildir.py_gettimeseqr   '   sl    	<tyy{#D8TNaNHTNXd^#s   AA7 7Bc                        e Zd Z fdZd Zd Zd Zd ZddZd Z	d Z
dd	Zd
 Zd ZddZddZd Zd Zd Zd Zd ZddZ xZS )MaildirFolderc                 l   || _         t        t        |   ||       || _        | j
                  j                  d| j                  z   dd      | _        | j                  rdnd| _	        	 t        j                  d| j                  z        | _        t        j                  d| j                  z   dz         | _        t        | j                         j!                  d	            j#                         | _        t&        j(                  j+                  | j-                         | j/                               | _        | j
                  j                  d
dd      }| j
                  j                  | j2                  d|      | _        d| _        t&        j(                  j                   | j6                  k(  rd| _        y y )NzAccount zmaildir-windows-compatibleF!:z	%s2,(\w*)z([^z,]*)zutf-8generalutime_from_header-_)sepsuperr   __init__rootconfiggetdefaultbooleanaccountnamewincompatibleinfoseprecompilere_flagmatchre_prefixmatchr   getvisiblenameencode	hexdigest
_foldermd5ospathjoingetrootgetname	_fullnamerepoconfname_utime_from_header	sep_subst)selfr!   namer   
repositoryutime_from_header_global	__class__s         r   r    zMaildirFolder.__init__7   sT   mT+D*=	![[::)))+GP"00scMJJ{T\\'AB !jj)=)FGd113::7CDNNPdllndllnE#';;#@#@*E$3 "&++"?"?24L#N 77;;$..( DN )    c                     | j                   S )zBReturn the absolute file path to the Maildir folder (sans cur|new))r4   r8   s    r   getfullnamezMaildirFolder.getfullnameS   s    ~~r=   c                      y)zRetrieve the current connections UIDVALIDITY value

        Maildirs have no notion of uidvalidity, so we just return a magic
        token.*    r?   s    r   get_uidvalidityzMaildirFolder.get_uidvalidityX   s    
 r=   c                     t         j                  |      }|sy|j                         }t        |      }|t	        j
                  |      k  ryy)zCheck to see if the given message is newer than date (a
        time_struct) according to the maildir name which should begin
        with a timestamp.TF)re_timestampmatchsearchgroupr   r   mktime)r8   messagenamer   timestampmatchtimestampstrtimestamplongs         r   _iswithintimezMaildirFolder._iswithintime_   sI    
 +11+>%++-L)4;;t,,r=   c                    dddt               f\  }}}}| j                  j                  |      }|r|j                  d      }d| j                  z  }||v }|r1t
        j                  |      }	|	rt        |	j                  d            }| j                  j                  |      }
|
r!t        d |
j                  d      D              }||||fS )a  Returns a messages file name components

        Receives the file name (without path) of a msg.  Usual format is
        '<%d_%d.%d.%s>,U=<%d>,FMD5=<%s>:2,<FLAGS>' (pointy brackets
        denoting the various components).

        If FMD5 does not correspond with the current folder MD5, we will
        return None for the UID & FMD5 (as it is not valid in this
        folder).  If UID or FMD5 can not be detected, we return `None`
        for the respective element.  If flags are empty or cannot be
        detected, we return an empty flags list.

        :returns: (prefix, UID, FMD5, flags). UID is a numeric "long"
            type. flags is a set() of Maildir flags.
        Nr   z,FMD5=%sc              3       K   | ]  }|  y wNrC   ).0cs     r   	<genexpr>z0MaildirFolder._parse_filename.<locals>.<genexpr>   s     7q7s   )	setr*   matchrH   r.   re_uidmatchrG   r   r)   )r8   filenameprefixuidfmd5flagsprefixmatch	folderstrfoldermatchuidmatch	flagmatchs              r   _parse_filenamezMaildirFolder._parse_filenamen   s    " $(tSU#: T5))//9 &&q)F0	8+ "))(3H(..+,%%,,X6	7IOOA$678EsD%''r=   c                 L   | j                         }i }g }d}dD ]]  t        j                  j                  | j	                               }|j                  fdt        j                  |      D               _ i }|D ]M  \  }	|	j                  d      rt        j                  j                  |	      }
|rOt        j                  j                  t        j                  j                  | j	                         |
            |kD  r| j                  |	      \  }}}}||}|dz  }n9t        j                  |	      }|s|}|dz  }nt        |j                  d            }||dkD  r||k  r|8| j                  |	|      s&| j                  |      ||<   |||   d<   |
||   d<   *| j                  |      ||<   |||   d<   |
||   d<   P |N|D cg c]
  }|dkD  s	| }}|r7t!        |      }t#        |j%                               D ]  }||kD  s	||   ||<    |S c c}w )	a-  Cache the message list from a Maildir.

        If min_date is set, this finds the min UID of all messages newer than
        min_date and uses it as the real cutoff for considering messages.
        This handles the edge cases where the date is much earlier than messages
        with similar UID's (e.g. the UID was reassigned much later).

        Maildir flags are:
            D (draft) F (flagged) R (replied) S (seen) T (trashed),
        plus lower-case letters for custom flags.
        :returns: dict that can be used as self.messagelist.
        )newcurc              3   &   K   | ]  }|f 
 y wrQ   rC   )rR   rX   dirannexs     r   rT   z,MaildirFolder._scanfolder.<locals>.<genexpr>   s      >! #H- >s   .r   r   r\   rX   )
getmaxsizer/   r0   r1   r@   extendlistdir
startswithgetsizerb   rW   rG   r   rH   rN   msglist_item_initializerminlistkeys)r8   min_datemin_uidmaxsizeretvalfilesnouidcounterfulldirnamedate_excludeesrX   filepathrY   rZ   r[   r\   r`   positive_uidsrh   s                    @r   _scanfolderzMaildirFolder._scanfolder   sK    //#& 	>H'',,t'7'7'98DKLL >%'ZZ%<> >	>
 "' '	3Hh""3'ww||Hh7HBGGOOGGLL!1!1!3X>@BIJ'+';';H'E$FCu{"!&--h7&C A%LhnnQ/0C"sQw3=#D,>,>x,R '+&C&CC&Hs#/4s#G,2:s#J/ #;;C@s',sG$*2sJ'O'	3P ,2>ScAgS>M>m, 3 3 56 :CW} '5S&9s:  ?s   
H! H!c                     t        | j                               t        |j                               k7  ryt        | j                         j	                               D ]  \  }}|d   |j                  |      k7  s y y)zdReturns True if the Maildir has changed

        Assumes cachemessagelist() has already been called Tr\   F)sortedgetmessageuidlistrq   getmessagelistitemsgetmessageflags)r8   statusfolderrZ   messages       r   quickchangedzMaildirFolder.quickchanged   sx    
 $((*+|55789"4#6#6#8#>#>#@A 	NS'w<#?#?#DD	 r=   c                     t               ddS )Nz/no-dir/no-such-file/)r\   rX   )rU   r8   rZ   s     r   ro   z&MaildirFolder.msglist_item_initializer   s    ,CDDr=   c                    | j                         rt| j                  j                  | j                  |        | j	                  ||      | _        | j                  j                  | j                  | | j                                y y )N)rs   rt   )ismessagelistemptyuiloadmessagelistr:   r}   messagelistmessagelistloadedgetmessagecount)r8   rs   rt   s      r   cachemessagelistzMaildirFolder.cachemessagelist   sl    ""$GG##DOOT:#//8?  0  ADGG%%dootT=Q=Q=ST	 %r=   c           
         | j                   |   d   }t        j                  j                  | j	                         |      }t        |d      }|j                         }|j                          | j                  d   j                  |      }t%        |j&                        dkD  r| j(                  j+                  d
j                  ||j&                               t-        d |j&                  D              rH| j(                  j+                  d       | j                  d   j                  | j/                  |            }	 |j1                  | j2                  d         }	|S |S #  t               }| j                  |      d   j                  dd      }t        dj                  |||d   j                  |d	         t        j                   j"                        xY w# t4        $ rd}| j7                  |d      }|d}t        dj                  ||t9        |      j                  |      t        j                   j"                        d}~ww xY w)z Returns an email message object.rX   rb8bitr   asciisurrogateescape)errorsz>Exception parsing message with ID ({}) from file ({}).
 {}: {}r   zUID {} has defects: {}c              3   <   K   | ]  }t        |t                y wrQ   )
isinstancer	   )rR   defects     r   rT   z+MaildirFolder.getmessage.<locals>.<genexpr>  s     `v:f&AB`s   z% ... applying multipart boundary fix.policyz
message-idNz<unknown-message-id>zDUID {} ({}) has defects preventing it from being processed!
  {}: {})r   r/   r0   r1   r@   openreadcloseparser
parsebytesr   _extract_message_iddecoder   format__name__ERRORMESSAGElendefectsr   warnany_quote_boundary_fixas_bytesr   UnicodeEncodeErrorgetmessageheadertype)
r8   rZ   rX   r{   fd	_fd_bytesrv   errmsg_idr   s
             r   
getmessagezMaildirFolder.getmessage  s    ##C(477<< 0 0 2H=(D!GGI	

kk&)44Y?V v~~"GGLL188fnnMN`QWQ_Q_`` DEV,778P8PQZ8[\8OO4;;v+>O? v7	0*C--i8;BB7RcBdF"QXXHc!foos1v? &&..0 0 & 8..v|D>3F&_ffc););SB(..668 88s&   -E  =G  A7G	I#AIIc                     | j                   |   d   }t        j                  j                  | j	                         |      }t        j                  j                  |      S )NrX   )r   r/   r0   r1   r@   getmtime)r8   rZ   rX   r{   s       r   getmessagetimezMaildirFolder.getmessagetime(  sI    ##C(477<< 0 0 2H=ww))r=   c                 X   |
t               }t        |      \  }}d||t        j                         t	        j
                         || j                  | j                  dj                  t        |            fz  }|j                  t        j                  j                  | j                        S )zCreates a new unique Maildir filename

        :param uid: The UID`None`, or a set of maildir flags
        :param flags: A set of maildir flags
        :param flags: (optional) Date
        :returns: String containing unique message filenamez%d_%d.%d.%s,U=%d,FMD5=%s%s2,%s )rU   r   r/   getpidsocketgethostnamer.   r&   r1   r   replacer0   r   r7   )r8   rZ   r\   r   timevaltimeseq	uniq_names          r   new_message_filenamez"MaildirFolder.new_message_filename-  s     =EE&t,4gryy{F4F4F4H$//4<<9OQQ	   dnn==r=   c                 ~   || j                   d   }n|}t        j                  j                  d|      }d}|rz|dz
  }	 t        j                  t        j                  j                  | j                         |      t        j                  t        j                  z  t        j                  z  d      }	 t        j&                  d      }|j)                  |j+                  |             |j-                          | j/                         rt        j0                  |       |j3                          |S # t        $ r}t        |d      s |j                  t        j                  k(  rR|rt        j                  d       Y d}~Jt        j                   j"                  }	t        d	|z  |	t%               d
          d}~ww xY w)a  Saves given message to the named temporary file in the
        'tmp' subdirectory of $CWD.

        Arguments:
        - filename: name of the temporary file;
        - msg: Email message object

        Returns: relative path to the temporary file
        that was created.Nr   tmp   r   i  EEXISTgq=
ףp?z"Unique filename %s already exists.   wbr   )r   r/   r0   r1   r   r@   O_EXCLO_CREATO_WRONLYOSErrorhasattrerrnor   r   sleepr   r   r   r   fdopenwriter   flushdofsyncfsyncr   )
r8   rX   msgr   output_policytmpnametriesr   eseveritys
             r   save_to_tmp_filezMaildirFolder.save_to_tmp_file>  s^    > KK/M"M'',,uh/ AIEWWRWW\\$*:*:*<gFYY3bkkA5J  YYr4 
]34

<<>HHRL

/  q(+77ell*

4( /55==H*< !"* 
1' '
 s   A3D1 1	F<:AF76F77F<c                    | j                   j                  d|||        |dk  r|S || j                  v r| j                  ||       |S d}| j                  dur'	 | j                  |d      }|| j                  |d      }| j                  |||	      }	| j                  |	|      }
| j                  d
u rY	 | j                  |d      }|Dt        j                  t        j                  j!                  | j#                         |
      ||f       | j%                  |      | j                  |<   || j                  |   d<   |
| j                  |   d<   | j                  ||       | j                   j'                  dd|z         |S # t        $ r.}ddlm}  |       }|j                  d||fz         Y d}~4d}~ww xY w# t        $ r-}ddlm}  |       }|j                  d||fz         Y d}~d}~ww xY w)zWrites a new message, with the specified uid.

        See folder/Base for detail. Note that savemessage() does not
        check against dryrun settings, so you need to ensure that
        savemessage is never called in a dryrun mode.maildirr   NFDatezDelivery-date)getglobaluizFUID %d has invalid date: %s
Not using message timestamp as file prefixr   Tz?UID %d has invalid date: %s
Not changing file modification timer\   rX   zsavemessage: returning uid %d)r   savemessager   savemessageflags_filename_use_mail_timestampget_message_date	Exceptionofflineimap.uir   r   r   r   r6   r/   utimer0   r1   r@   ro   debug)r8   rZ   r   r\   rtimemessage_timestampr   r   r   rJ   r   r   s               r   r   zMaildirFolder.savemessageq  s    	IsE487J$"""!!#u-J !,,E9Q$($9$9#v$F!$,(,(=(=_).% //UAR/S''S9""d*J,,S&9#HHRWW\\$*:*:*<gF"D\+ !% = =c B).g&,3j)c5)i!@3!FG
A  Q 7 ] EHKQxP Q QQ$  J 7 ] >ADaI J JJs1   &F 4AF; 	F8
#F33F8;	G1#G,,G1c                 &    | j                   |   d   S )Nr\   )r   r   s     r   r   zMaildirFolder.getmessageflags  s    $W--r=   c           	         || j                   v sJ | j                   |   d   }t        j                  j                  |      \  }}d|v rdnd}|| j                   |   d   k7  rh| j                  j                  |      }|r|dt        |j                                 }| j                  ddj                  t        |            }||z  }t        j                  j                  ||      }||k7  r	 t        j                  t        j                  j                  | j                         |      t        j                  j                  | j                         |             || j                   |   d<   || j                   |   d<   yy# t        $ rH}	t        d	|d
|d|	j                  t        j                   j"                  t%               d         d}	~	ww xY w)a6  Sets the specified message's flags to the given set.

        This function moves the message to the cur or new subdir,
        depending on the 'S'een flag.

        Note that this function does not check against dryrun settings,
        so you need to ensure that it is never called in a
        dryrun mode.rX   Srf   re   r\   Nz2,r   Can't rename file '' to '': r   )r   r/   r0   splitr)   rG   r   rH   r&   r1   r   renamer@   r   r   r   r   FOLDERr   )
r8   rZ   r\   oldfilename
dir_prefixrX   	infomatchinfostrnewfilenamer   s
             r   r   zMaildirFolder.savemessageflags  s    d&&&&&&&s+J7!ww}}[9
H!UlU
D$$S)'22 ))00:I#$<c)//*;&<%<="&,,u0FGGHggll:x8+%#		"'',,t'7'7'9;G'',,t'7'7'9;GI .3DS!'*0;DS!*- &  #& +qww8$**11JqM	# ##s   0A.F 	GAGGc                    || j                   vr't        d|z  t        j                  j                        ||k(  ry| j                   |   d   }t        j
                  j                  |      \  }}| j                  |      }t        j
                  j                  || j                  ||            }t	        j                  t        j
                  j                  | j                         |      t        j
                  j                  | j                         |             | j                   |   | j                   |<   || j                   |   d<   | j                   |= y)ap  Change the message from existing uid to new_uid

        This will not update the statusfolder UID, you need to do that yourself.
        :param uid: Message UID
        :param new_uid: (optional) If given, the old UID will be changed
                        to a new UID. The Maildir backend can implement this as
                        an efficient rename.
        z$Cannot change unknown Maildir UID %sNrX   )r   r   r   r   r/   r0   r   r   r1   r   r   r@   )r8   rZ   new_uidr   r   rX   r\   r   s           r   change_message_uidz MaildirFolder.change_message_uid  s%    d&&&"#IC#O#3#9#9#A#AC C'>&&s+J7!ww}}[9
H$$S) ggll:#'#<#<We#LN
		"'',,t//1;?'',,t//1;?	A$($4$4S$9!0;!*-S!r=   c                    | j                   |   d   }t        j                  j                  | j	                         |      }	 t        j
                  |       | j                   |= y# t        $ rb | j                         }||v rK||   d   }t        j                  j                  | j	                         |      }t        j
                  |       Y xw xY w)zUnlinks a message file from the Maildir.

        :param uid: UID of a mail message
        :type uid: String
        :return: Nothing, or an Exception if UID but no corresponding file
                 found.
        rX   N)r   r/   r0   r1   r@   unlinkr   r}   )r8   rZ   rX   r{   
newmsglists        r   deletemessagezMaildirFolder.deletemessage  s     ##C(477<< 0 0 2H=	$IIh c"  	$))+Jj %c?:677<<(8(8(:HE		(#	$s   A% %A(CCc           
         t        | j                        j                         }| j                         }t	        |j                               D ]J  \  }}t        j                  j                  | j                         |d         }t        j                  d|      }| | j                  j                  dd|z         p|j                  d      |k(  rx| j                  j                  d|d| j                   d	       |r|j#                  d
|j                  d      z   d
| j                   z         }	 t        j$                  ||       |j                  d      | j                   k7  s| j                  j3                  d|d|d| j                   d       M y# t&        $ rH}	t)        d|d|d|	j*                  t(        j,                  j.                  t1               d         d}	~	ww xY w)z{Migrate FMD5 hashes from versions prior to 6.3.5

        :param dryrun: Run in dry run mode
        :return: None
        rX   zFMD5=([a-fA-F0-9]+)Nr   z'File `%s' doesn't have an FMD5 assignedr   zMigrating file `z' to FMD5 `'zFMD5=r   r   r   r   zInconsistent FMD5 for file `z': Neither `z' nor `z' found)r   r9   r-   r}   rq   r   r/   r0   r1   r@   r'   rG   r   r   rH   infor.   r   r   r   r   r   r   r   r   r   )
r8   dryrunoldfmd5msglistmkeymvaluerX   rV   r   r   s
             r   migratefmd5zMaildirFolder.migratefmd5  s    dii.**,""$ 1 	ELD&ww||D$4$4$6z8JKHII3X>E}iG ()* Q7* ($//; <"*"2"2%++a.0'DOO2K#MK+		(K8 Q4??2 ('4??D E/	E # +.%{AGG=,2299$JqM	+ ++s   2F	G-%AG((G-)NNrQ   )F)r   
__module____qualname__r    r@   rD   rN   rb   r}   r   ro   r   r   r   r   r   r   r   r   r   r   r  __classcell__)r<   s   @r   r   r   6   sl    !8
'(RL^EU$N*
>"0f=@.'<T"<$,!Er=   r   rQ   )r   r   r   r'   r/   sysr   	threadingr   hashlibr   offlineimapr   Baser   email.errorsr	   r(   rW   rF   r   r   r   r   rC   r=   r   <module>r     sl   $    	 	    (  4 bjj$BJJw' 6~EJ ~Er=   