import mimetypes import magic from pathlib import Path from django.core.exceptions import ValidationError from .models import Attachment,AttachmentPolicy TYPE_EXT={"IMAGE":{".jpg",".jpeg",".png",".gif",".webp"},"VIDEO":{".mp4",".webm",".mov"},"VOICE":{".webm",".ogg",".mp3",".m4a",".wav"},"PDF":{".pdf"},"WORD":{".doc",".docx"},"EXCEL":{".xls",".xlsx"},"ZIP":{".zip"},"RAR":{".rar"}} def classify(ext,mime): if ext==".webm" and str(mime).startswith("audio/"): return "VOICE" for kind,extensions in TYPE_EXT.items(): if ext in extensions:return kind return "OTHER" def save_attachments(message,files,user): files=list(files); result=[] counts={} for upload in files: ext=Path(Path(upload.name).name).suffix.lower(); kind=classify(ext,getattr(upload,"content_type", "")); counts[kind]=counts.get(kind,0)+1 for kind,count in counts.items(): policy=AttachmentPolicy.objects.filter(attachment_type=kind,is_enabled=True).first() if not policy: raise ValidationError(f"نوع فایل {kind} مجاز نیست.") if count>policy.max_files_per_message: raise ValidationError(f"تعداد فایل‌های {kind} بیش از حد مجاز است.") for upload in files: name=Path(upload.name).name[:255]; ext=Path(name).suffix.lower(); declared=getattr(upload,"content_type","") or mimetypes.guess_type(name)[0] or "application/octet-stream" head=upload.read(8192); upload.seek(0); detected=magic.from_buffer(head,mime=True) or "application/octet-stream"; kind=classify(ext,declared) policy=AttachmentPolicy.objects.filter(attachment_type=kind,is_enabled=True).first() if not policy: raise ValidationError(f"نوع فایل {ext or name} مجاز نیست.") if upload.size>policy.max_file_size: raise ValidationError(f"حجم فایل {name} بیش از حد مجاز است.") if policy.allowed_extensions and ext not in policy.allowed_extensions: raise ValidationError("پسوند فایل مجاز نیست.") if policy.allowed_mime_types and detected not in policy.allowed_mime_types: raise ValidationError("نوع محتوای واقعی فایل مجاز نیست.") compatible={"IMAGE":detected.startswith("image/"),"VIDEO":detected.startswith("video/"),"VOICE":detected.startswith("audio/") or detected in {"video/webm","application/ogg"},"PDF":detected=="application/pdf","ZIP":detected in {"application/zip","application/x-zip-compressed"}}.get(kind,True) if not compatible: raise ValidationError(f"محتوای واقعی فایل {name} با پسوند آن سازگار نیست.") obj=Attachment(message=message,file=upload,original_name=name,stored_name="pending",relative_path="pending",attachment_type=kind,mime_type=detected,extension=ext,file_size=upload.size,uploaded_by=user); obj.save(); obj.stored_name=Path(obj.file.name).name; obj.relative_path=obj.file.name; obj.save(update_fields=["stored_name","relative_path"]); result.append(obj) return result