comparison MoinMoin/action/AttachFile.py @ 3712:b6dcdf55795e

AttachFile action: catch RuntimeError when someone tries to unzip a defective archive
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Sat, 14 Jun 2008 01:49:34 +0200
parents ce632fad1b03
children 0248e23277ef d9de4fa12f23 8cb4d34ccbc1
comparison
equal deleted inserted replaced
3710:4819ec465818 3712:b6dcdf55795e
857 return _('You are not allowed to unzip attachments of this page.') 857 return _('You are not allowed to unzip attachments of this page.')
858 858
859 if not filename: 859 if not filename:
860 return # error msg already sent in _access_file 860 return # error msg already sent in _access_file
861 861
862 if not zipfile.is_zipfile(fpath): 862 try:
863 return _('The file %(filename)s is not a .zip file.') % {'filename': filename} 863 if not zipfile.is_zipfile(fpath):
864 864 return _('The file %(filename)s is not a .zip file.') % {'filename': filename}
865 # determine how which attachment names we have and how much space each is occupying 865
866 curr_fsizes = dict([(f, size(request, pagename, f)) for f in _get_files(request, pagename)]) 866 # determine how which attachment names we have and how much space each is occupying
867 867 curr_fsizes = dict([(f, size(request, pagename, f)) for f in _get_files(request, pagename)])
868 # Checks for the existance of one common prefix path shared among 868
869 # all files in the zip file. If this is the case, remove the common prefix. 869 # Checks for the existance of one common prefix path shared among
870 # We also prepare a dict of the new filenames->filesizes. 870 # all files in the zip file. If this is the case, remove the common prefix.
871 zip_path_sep = '/' # we assume '/' is as zip standard suggests 871 # We also prepare a dict of the new filenames->filesizes.
872 fname_index = None 872 zip_path_sep = '/' # we assume '/' is as zip standard suggests
873 mapping = [] 873 fname_index = None
874 new_fsizes = {} 874 mapping = []
875 zf = zipfile.ZipFile(fpath) 875 new_fsizes = {}
876 for zi in zf.infolist(): 876 zf = zipfile.ZipFile(fpath)
877 name = zi.filename 877 for zi in zf.infolist():
878 if not name.endswith(zip_path_sep): # a file (not a directory) 878 name = zi.filename
879 if fname_index is None: 879 if not name.endswith(zip_path_sep): # a file (not a directory)
880 fname_index = name.rfind(zip_path_sep) + 1 880 if fname_index is None:
881 path = name[:fname_index] 881 fname_index = name.rfind(zip_path_sep) + 1
882 if (name.rfind(zip_path_sep) + 1 != fname_index # different prefix len 882 path = name[:fname_index]
883 or 883 if (name.rfind(zip_path_sep) + 1 != fname_index # different prefix len
884 name[:fname_index] != path): # same len, but still different 884 or
885 mapping = [] # zip is not acceptable 885 name[:fname_index] != path): # same len, but still different
886 break 886 mapping = [] # zip is not acceptable
887 if zi.file_size >= request.cfg.unzip_single_file_size: # file too big 887 break
888 mapping = [] # zip is not acceptable 888 if zi.file_size >= request.cfg.unzip_single_file_size: # file too big
889 break 889 mapping = [] # zip is not acceptable
890 finalname = name[fname_index:] # remove common path prefix 890 break
891 finalname = finalname.decode(config.charset, 'replace') # replaces trash with \uFFFD char 891 finalname = name[fname_index:] # remove common path prefix
892 mapping.append((name, finalname)) 892 finalname = finalname.decode(config.charset, 'replace') # replaces trash with \uFFFD char
893 new_fsizes[finalname] = zi.file_size 893 mapping.append((name, finalname))
894 894 new_fsizes[finalname] = zi.file_size
895 # now we either have an empty mapping (if the zip is not acceptable), 895
896 # an identity mapping (no subdirs in zip, just all flat), or 896 # now we either have an empty mapping (if the zip is not acceptable),
897 # a mapping (origname, finalname) where origname is the zip member filename 897 # an identity mapping (no subdirs in zip, just all flat), or
898 # (including some prefix path) and finalname is a simple filename. 898 # a mapping (origname, finalname) where origname is the zip member filename
899 899 # (including some prefix path) and finalname is a simple filename.
900 # calculate resulting total file size / count after unzipping: 900
901 if overwrite: 901 # calculate resulting total file size / count after unzipping:
902 curr_fsizes.update(new_fsizes) 902 if overwrite:
903 total = curr_fsizes 903 curr_fsizes.update(new_fsizes)
904 else: 904 total = curr_fsizes
905 new_fsizes.update(curr_fsizes)
906 total = new_fsizes
907 total_count = len(total)
908 total_size = sum(total.values())
909
910 if not mapping:
911 msg = _("Attachment '%(filename)s' not unzipped because some files in the zip "
912 "are either not in the same directory or exceeded the single file size limit (%(maxsize_file)d kB)."
913 ) % {'filename': filename,
914 'maxsize_file': request.cfg.unzip_single_file_size / 1000, }
915 elif total_size > request.cfg.unzip_attachments_space:
916 msg = _("Attachment '%(filename)s' not unzipped because it would have exceeded "
917 "the per page attachment storage size limit (%(size)d kB).") % {
918 'filename': filename,
919 'size': request.cfg.unzip_attachments_space / 1000, }
920 elif total_count > request.cfg.unzip_attachments_count:
921 msg = _("Attachment '%(filename)s' not unzipped because it would have exceeded "
922 "the per page attachment count limit (%(count)d).") % {
923 'filename': filename,
924 'count': request.cfg.unzip_attachments_count, }
925 else:
926 not_overwritten = []
927 for origname, finalname in mapping:
928 try:
929 # Note: reads complete zip member file into memory. ZipFile does not offer block-wise reading:
930 add_attachment(request, pagename, finalname, zf.read(origname), overwrite)
931 except AttachmentAlreadyExists:
932 not_overwritten.append(finalname)
933 if not_overwritten:
934 msg = _("Attachment '%(filename)s' partially unzipped (did not overwrite: %(filelist)s).") % {
935 'filename': filename,
936 'filelist': ', '.join(not_overwritten), }
937 else: 905 else:
938 msg = _("Attachment '%(filename)s' unzipped.") % {'filename': filename} 906 new_fsizes.update(curr_fsizes)
907 total = new_fsizes
908 total_count = len(total)
909 total_size = sum(total.values())
910
911 if not mapping:
912 msg = _("Attachment '%(filename)s' not unzipped because some files in the zip "
913 "are either not in the same directory or exceeded the single file size limit (%(maxsize_file)d kB)."
914 ) % {'filename': filename,
915 'maxsize_file': request.cfg.unzip_single_file_size / 1000, }
916 elif total_size > request.cfg.unzip_attachments_space:
917 msg = _("Attachment '%(filename)s' not unzipped because it would have exceeded "
918 "the per page attachment storage size limit (%(size)d kB).") % {
919 'filename': filename,
920 'size': request.cfg.unzip_attachments_space / 1000, }
921 elif total_count > request.cfg.unzip_attachments_count:
922 msg = _("Attachment '%(filename)s' not unzipped because it would have exceeded "
923 "the per page attachment count limit (%(count)d).") % {
924 'filename': filename,
925 'count': request.cfg.unzip_attachments_count, }
926 else:
927 not_overwritten = []
928 for origname, finalname in mapping:
929 try:
930 # Note: reads complete zip member file into memory. ZipFile does not offer block-wise reading:
931 add_attachment(request, pagename, finalname, zf.read(origname), overwrite)
932 except AttachmentAlreadyExists:
933 not_overwritten.append(finalname)
934 if not_overwritten:
935 msg = _("Attachment '%(filename)s' partially unzipped (did not overwrite: %(filelist)s).") % {
936 'filename': filename,
937 'filelist': ', '.join(not_overwritten), }
938 else:
939 msg = _("Attachment '%(filename)s' unzipped.") % {'filename': filename}
940 except RuntimeError, err:
941 # We don't want to crash with a traceback here (an exception
942 # here could be caused by an uploaded defective zip file - and
943 # if we crash here, the user does not get a UI to remove the
944 # defective zip file again).
945 # RuntimeError is raised by zipfile stdlib module in case of
946 # problems (like inconsistent slash and backslash usage in the
947 # archive).
948 logging.exception("An exception within zip file attachment handling occurred:")
949 msg = _("A severe error occurred:") + ' ' + str(err)
939 950
940 upload_form(pagename, request, msg=wikiutil.escape(msg)) 951 upload_form(pagename, request, msg=wikiutil.escape(msg))
941 952
942 953
943 def send_viewfile(pagename, request): 954 def send_viewfile(pagename, request):