U
    <gH                  	   @   s  d Z ddlZddlmZ ddlmZmZmZmZ ddl	m
Z
mZ ddlmZmZ ddlmZmZ dd	lmZ dd
lmZmZmZmZ ddlmZmZmZ ddlmZmZm Z m!Z! ddl"m#Z#m$Z$ ddl%m&Z&m'Z'm(Z(m)Z) e*e+Z,dddgZ-edddZ.e
j/e
j/dddZ0G dd deZ1e1e e# 2ej3e 2ej3e 2ej3e 2ej3e$ 2ej3gee  e! e gddZ4e1g ddZ5dS )zF
Module defining pyHanko's standard difference policy implementation.
    N)defaultdict)IteratorListOptionalUnion)genericmisc)HistoricalResolverPdfFileReader)FieldMDPSpecMDPPerm   )FormUpdatingRule)
DiffPolicy
DiffResultModificationLevelSuspiciousModification)CatalogModificationRuleObjectStreamRuleXrefStreamRule)DSSCompareRuleGenericFieldModificationRuleSigFieldCreationRuleSigFieldModificationRule)DocInfoRuleMetadataUpdateRule)ApprovalTypeContextQualifiedWhitelistRuleReferenceUpdateStandardDiffPolicyDEFAULT_DIFF_POLICYNO_CHANGES_DIFF_POLICY)hist_revc                    s      } j jd }t t }|D ]$}||rD|| q*| q*ttj	 d fdd}| }|rzt
|}W n tk
r   Y qY nX | j| jd8 }qp|S )za
    Within a revision, find new refs that can't be reached from refs in the
    older ones.
    r   )returnc                  3   s     j V  D ]}  | V  qd S N)Ztrailer_view)_refr#   Zupdated_old_refs G/tmp/pip-unpacked-wheel-w101_d3s/pyhanko/sign/diff_analysis/policies.py_objs_to_checkR   s    z%_find_orphans.<locals>._objs_to_check)Zsince_revision)explicit_refs_in_revisionreaderget_historical_resolverrevisionsetZis_ref_availableaddr   r   	PdfObjectnextStopIterationZcollect_dependencies)r#   Znew_refspreviousZcandidate_orphansrefr*   Zobj_iterobjr(   r'   r)   _find_orphans1   s(    

 
r7   )
old_object
new_objectc                    s0  t jt jt jt jt jt jf}|D ]$}t|r t |o> k  S q t jt j	f}t|rrt |opj
 j
kS tt jrt t jot tkotdd t D S tt jrt t jrֈ jjksdS tt jr(t t jr   ksdS t fdd  D S td S )Nc                 s   s   | ]\}}t ||V  qd S r%   )_is_id).0xyr(   r(   r)   	<genexpr>}   s     z_is_id.<locals>.<genexpr>Fc                 3   s$   | ]}t | |V  qd S r%   )r:   Zraw_get)r;   kr9   r8   r(   r)   r>      s   )r   ZNumberObjectZFloatObjectZBooleanObjectZ
NameObjectZ
NullObjectZIndirectObject
isinstanceZTextStringObjectZByteStringObjectoriginal_bytesZArrayObjectlenallzipZStreamObjectZencoded_dataZDictionaryObjectkeysNotImplementedError)r8   r9   
primitivesZprimstringsr(   r@   r)   r:   e   sJ    






r:   c                   @   sz   e Zd ZdZdee ee dddZde	e	ee
 ee eddd	Zdeeee	f ee
 ee eeef d
ddZdS )r    a  
    Run a list of rules to analyse the differences between two revisions.

    :param global_rules:
        The :class:`.QualifiedWhitelistRule` objects encoding the rules to
        apply.
    :param form_rule:
        The :class:`.FormUpdatingRule` that adjudicates changes to form fields
        and their values.
    :param reject_object_freeing:
        Always fail revisions that free objects that existed prior to signing.

        .. note::
            PyHanko resolves freed references to the ``null`` object in PDF,
            and a freeing instruction in a cross-reference section is
            always registered as a change that needs to be approved, regardless
            of the value of this setting.

            It is theoretically possible for a rule to permit deleting content,
            in which case allowing objects to be freed might be reasonable.
            That said, pyHanko takes the conservative default position to reject
            all object freeing instructions as suspect.
    :param ignore_orphaned_objects:
        Some PDF writers create objects that aren't used anywhere (tsk tsk).
        Since those don't affect the "actual" document content, they can usually
        be ignored. If ``True``, newly created orphaned objects will be
        cleared at level :attr:`.ModificationLevel.LTA_UPDATES`.
        Default is ``True``.
    :param ignore_orphaned_objects:
        Some PDF writers overwrite objects with identical copies.
        Pointless and annoying, but also more or less harmless.
    Tglobal_rules	form_rulec                 C   s"   || _ || _|| _|| _|| _d S r%   )rK   rL   reject_object_freeingignore_orphaned_objectsignore_identical_objects)selfrK   rL   rM   rN   rO   r(   r(   r)   __init__   s
    zStandardDiffPolicy.__init__N)oldnewfield_mdp_specdoc_mdpr$   c                    s  |t jkrtd | jr6 }|r6td| d tt	 fdd}| j
rxtD ]} tj | qbt| ttd fdd}| jD ]$}	|	D ]\}
}||
| qqt	 }| jrh| j}fd	d
}|D ]v\}
}||
| |j}|d k	rF|jsF||r<td|j d| d|| |d k	r|jstd|j dq tj  }| tj  }| tj  }jj| jr t	fdd|D }|r tdj dj d|  | | | | | | |rt!"ddd |D }tdj| dd # D }dj dd$dd |D  d}|rdj dj d d$| }d!||f }t| t|n"|rtj}
n|rtj}
ntj}
t%|
|d"S )#NzzStandardDiffPolicy was not designed to support DocMDP level 3 (MDPPerm.ANNOTATE). Unexpected validation results may occur.z	The refs zn were freed in the revision provided. The configured difference analysis policy does not allow object freeing.c                  3   sF       D ]4} | }|rfdd|D }| tj|ffV  qd S )Nc                    s   h | ]}t  |qS r(   )r   Zfrom_absoluter;   p)rR   r(   r)   	<setcomp>   s     zDStandardDiffPolicy.apply.<locals>._init_multi_lut.<locals>.<setcomp>)Z_load_reverse_xref_cacheZ_get_usages_of_refr   NONE)Znew_refusagesZcontexts)	new_xrefsrR   r(   r)   _init_multi_lut   s    
z1StandardDiffPolicy.apply.<locals>._init_multi_lut)_level_updc                    s    j }zv| \}} j}|tjkr,t }n.|tjkrNt fdd|D }n| j t|| } | |f|< |rzW d S W n t	k
r   Y nX |  
| d S )Nc                 3   s   | ]}|j  jkr|V  qd S r%   )Zrelative_viewcontext_checked)r;   ctxr^   r(   r)   r>     s   z?StandardDiffPolicy.apply.<locals>.ingest_ref.<locals>.<genexpr>)updated_refZapproval_typer   ZBLANKET_APPROVEr/   ZAPPROVE_RELATIVE_CONTEXTdiscardr_   maxKeyErrorr0   )r]   r^   r5   Zcurrent_max_levelrZ   Zupd_type)	explainedold_usages_to_clearra   r)   
ingest_ref   s$    


	

z,StandardDiffPolicy.apply.<locals>.ingest_refc                    s    d k	o  | S r%   )	is_locked)Zfq_name)rT   r(   r)   ri   !  s    z+StandardDiffPolicy.apply.<locals>.is_lockedz
Update of z' is not allowed because the form field z is locked.zL is only allowed after an approval signature, not a certification signature.c                 3   s6   | ].} |jd k	rt| |r|V  qd S r%   )Zget_historical_refr.   r:   )r;   Zunex_ref)rS   rR   
xref_cacher(   r)   r>   A  s   z+StandardDiffPolicy.apply.<locals>.<genexpr>z5Found identical overridden objects between revisions  and z+; following no-op changes will be ignored: 
c                 s   s.   | ]&}d t |t | dd f V  qdS )z%s:%s...Ni,  )reprZ
get_objectr;   r<   r(   r(   r)   r>   U  s   z$Unexplained xrefs in revision %d:
%sc              	   S   s>   g | ]6\}\}}|rd t | dddd |D  dqS )z - z is also used in , c                 s   s   | ]}t |V  qd S r%   )strrV   r(   r(   r)   r>   ^  s     z6StandardDiffPolicy.apply.<locals>.<listcomp>.<genexpr>z in the prior revision.)rm   join)r;   r5   _Zpaths_remainingr(   r(   r)   
<listcomp>]  s   
z,StandardDiffPolicy.apply.<locals>.<listcomp>z(There are unexplained xrefs in revision z: ro   c                 s   s   | ]}t |V  qd S r%   )rm   rn   r(   r(   r)   r>   e  s     .zSome objects from revision z were replaced in revision z  without precise justification:
z%s
%s)modification_levelchanged_form_fields)&r   ZANNOTATEloggerwarningrM   Zrefs_freed_in_revisionr   r+   r   r/   rN   r7   r   LTA_UPDATESr0   dictr   rK   Zapply_qualifiedrL   apply
field_nameZvalid_when_lockedrb   Zvalid_when_certifyingZFORM_FILLINGZANNOTATIONSr,   xrefsrO   debugr.   difference_updater   ZLazyJoinitemsrq   r   )rP   rR   rS   rT   rU   Zfreedr\   r&   rh   ZrulelevelZupdrv   Zform_changesri   Zfur|   Zunexplained_ltaZunexplained_formfillZunexplained_annotZidentical_objsmsgZunexplained_overrideserr_msgZunchecked_paths_msgr(   )rf   rT   rS   r[   rR   rg   rj   r)   r{      s    

	
 






  "

 zStandardDiffPolicy.apply)r,   base_revisionrT   rU   r$   c                 C   s   t  }|jj}tj}t|tr.||}|}	n
|}|j}	t	|	d |D ]}
z| j
|||
||d}W nH tk
r } z*tjd|	 d|
 |d | W Y   S d}~X Y nX t||j}||jO }qFt||S )z
        Implementation of :meth:`.DiffPolicy.review_file` that reviews
        each intermediate revision between the base revision and the current one
        individually.
        r   )rR   rS   rT   rU   z)Error in diff operation between revision rk   )exc_infoN)r/   r}   Ztotal_revisionsr   rY   rA   intr-   r.   ranger{   r   rw   rx   rd   ru   rv   r   )rP   r,   r   rT   rU   rv   Z	rev_countZcurrent_maxZbase_rev_resolverZbase_revision_nor.   Zdiff_resulter(   r(   r)   review_file}  s2    


zStandardDiffPolicy.review_file)TTT)NN)NN)__name__
__module____qualname____doc__r   r   r   r   rQ   r	   r   r   r   r{   r
   r   r   r   r   r(   r(   r(   r)   r       s6   %      =  

)Zfield_rulesrJ   )6r   loggingcollectionsr   typingr   r   r   r   Zpyhanko.pdf_utilsr   r   Zpyhanko.pdf_utils.readerr	   r
   Zpyhanko.sign.fieldsr   r   Zform_rules_apir   Z
policy_apir   r   r   r   Zrules.file_structure_rulesr   r   r   Zrules.form_field_rulesr   r   r   r   Zrules.metadata_rulesr   r   Z	rules_apir   r   r   r   	getLoggerr   rw   __all__r7   r1   r:   r    Zas_qualifiedry   r!   r"   r(   r(   r(   r)   <module>   sN   
40  %&