Work on download files(channels)
This commit is contained in:
@@ -3823,43 +3823,44 @@ public class ActionHandler {
|
|||||||
String fileName = sanitizeFileName(rawName);
|
String fileName = sanitizeFileName(rawName);
|
||||||
long declaredSize = att.optLong("file_size", 0L);
|
long declaredSize = att.optLong("file_size", 0L);
|
||||||
|
|
||||||
// ~/Downloads/TeleSock/<chatDisplayOrId or ChatUUID>/
|
// ~/Downloads/TeleSock/<Account>/<Chat>/
|
||||||
String folderName = (chat.getDisplayId() != null && !chat.getDisplayId().isBlank())
|
String accFolder = accountFolderName(); // ← از یوزر لاگینشده
|
||||||
? chat.getDisplayId() : chat.getId().toString();
|
String chatFolder = chatFolderName(chat); // ← برای پرایوت: اسم طرف مقابل
|
||||||
Path saveDir = Paths.get(System.getProperty("user.home"), "Downloads", "TeleSock", folderName);
|
Path saveDir = Paths.get(System.getProperty("user.home"),
|
||||||
|
"Downloads", "TeleSock", accFolder, chatFolder);
|
||||||
|
|
||||||
try { Files.createDirectories(saveDir); } catch (IOException e) {
|
// دیباگ اختیاری
|
||||||
|
System.out.println("👤 AccountFolder = " + accFolder);
|
||||||
|
System.out.println("💬 ChatFolder = " + chatFolder);
|
||||||
|
System.out.println("📁 SaveDir = " + saveDir);
|
||||||
|
|
||||||
|
try { Files.createDirectories(saveDir); }
|
||||||
|
catch (IOException e) {
|
||||||
System.out.println("❌ Cannot create folder: " + saveDir + " -> " + e.getMessage());
|
System.out.println("❌ Cannot create folder: " + saveDir + " -> " + e.getMessage());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// اگر قبلاً دانلود شده (و فایل واقعاً وجود دارد)
|
// اگر قبلاً دانلود شده (و فایل واقعاً وجود دارد) — ایندکس مخصوص همین اکانت
|
||||||
try {
|
DownloadsIndex di = Session.downloadsIndex;
|
||||||
Path existing = Session.downloadsIndex.find(mediaKey);
|
if (di != null) {
|
||||||
|
Path existing = di.find(mediaKey);
|
||||||
if (existing != null) {
|
if (existing != null) {
|
||||||
System.out.println("✅ Already downloaded: " + existing);
|
System.out.println("✅ Already downloaded: " + existing);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (IllegalStateException notInit) {
|
|
||||||
System.out.println("⚠️ DownloadsIndex not initialized. Call DownloadsIndex.init(<internal_uuid>) after login.");
|
|
||||||
// ادامه میدهیم؛ فقط کش نمیشود.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// جلوگیری از overwrite با انتخاب نام یکتا
|
// جلوگیری از overwrite
|
||||||
Path target = uniquePath(saveDir, fileName);
|
Path target = uniquePath(saveDir, fileName);
|
||||||
|
|
||||||
|
// دانلود روی همان سوکت
|
||||||
// دانلود روی همان سوکت: Listener را موقتاً متوقف کن
|
|
||||||
TelegramClient.mediaBusy.set(true);
|
TelegramClient.mediaBusy.set(true);
|
||||||
try {
|
try {
|
||||||
Path saved = TelegramClient.getDownloader().download(mediaKey, saveDir, target.getFileName().toString());
|
Path saved = TelegramClient.getDownloader()
|
||||||
|
.download(mediaKey, saveDir, target.getFileName().toString());
|
||||||
|
|
||||||
long sizeToRecord = declaredSize > 0 ? declaredSize : Files.size(saved);
|
long sizeToRecord = declaredSize > 0 ? declaredSize : Files.size(saved);
|
||||||
try {
|
if (di != null) di.put(mediaKey, saved, sizeToRecord);
|
||||||
Session.downloadsIndex.put(mediaKey, saved, sizeToRecord);
|
|
||||||
} catch (IllegalStateException notInit) {
|
|
||||||
// اگر init نشده بود، تنها کش نمیکنیم
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("✅ Saved to: " + saved + " (" + humanSize(sizeToRecord) + ")");
|
System.out.println("✅ Saved to: " + saved + " (" + humanSize(sizeToRecord) + ")");
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
@@ -3869,6 +3870,9 @@ public class ActionHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// نام یکتا اگر فایل موجود است: name.png -> name (1).png
|
// نام یکتا اگر فایل موجود است: name.png -> name (1).png
|
||||||
private static Path uniquePath(Path dir, String fileName) {
|
private static Path uniquePath(Path dir, String fileName) {
|
||||||
Path p = dir.resolve(fileName);
|
Path p = dir.resolve(fileName);
|
||||||
|
|||||||
@@ -27,4 +27,4 @@ public final class DownloadIndexRegistry {
|
|||||||
}));
|
}));
|
||||||
HOOK_REGISTERED = true;
|
HOOK_REGISTERED = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
package org.to.telegramfinalproject.Client;
|
package org.to.telegramfinalproject.Client;
|
||||||
|
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|||||||
@@ -75,4 +75,4 @@ public final class SocketMediaDownloader {
|
|||||||
while (java.nio.file.Files.exists(dir.resolve(base + " (" + i + ")" + ext))) i++;
|
while (java.nio.file.Files.exists(dir.resolve(base + " (" + i + ")" + ext))) i++;
|
||||||
return dir.resolve(base + " (" + i + ")" + ext);
|
return dir.resolve(base + " (" + i + ")" + ext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,7 @@ package org.to.telegramfinalproject.Database;
|
|||||||
import org.to.telegramfinalproject.Models.FileAttachment;
|
import org.to.telegramfinalproject.Models.FileAttachment;
|
||||||
import org.to.telegramfinalproject.Models.MediaRow;
|
import org.to.telegramfinalproject.Models.MediaRow;
|
||||||
import org.to.telegramfinalproject.Models.Message;
|
import org.to.telegramfinalproject.Models.Message;
|
||||||
|
import org.to.telegramfinalproject.Utils.ChannelPermissionUtil;
|
||||||
|
|
||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@@ -1355,7 +1356,7 @@ public class MessageDatabase {
|
|||||||
return GroupDatabase.isMember(mr.receiverId, requester);
|
return GroupDatabase.isMember(mr.receiverId, requester);
|
||||||
|
|
||||||
case "channel":
|
case "channel":
|
||||||
return ChannelDatabase.isUserInChannel(mr.receiverId, requester);
|
return ChannelPermissionUtil.isUserInChannel(requester, mr.receiverId);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -19,4 +19,6 @@ public class MediaRow {
|
|||||||
public Integer durationSeconds; //for audio only
|
public Integer durationSeconds; //for audio only
|
||||||
public String thumbnailUrl;
|
public String thumbnailUrl;
|
||||||
public String fileUrl; //display link
|
public String fileUrl; //display link
|
||||||
|
public String chatType;
|
||||||
|
public UUID chatId;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,15 @@ public class ClientHandler implements Runnable {
|
|||||||
private final AuthService authService = new AuthService();
|
private final AuthService authService = new AuthService();
|
||||||
private User currentUser;
|
private User currentUser;
|
||||||
|
|
||||||
|
// ClientHandler.java
|
||||||
|
private static void log(String msg) {
|
||||||
|
System.out.println(java.time.LocalDateTime.now() + " [ClientHandler] " + msg);
|
||||||
|
}
|
||||||
|
private static void logf(String fmt, Object... args) {
|
||||||
|
log(String.format(fmt, args));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public ClientHandler(Socket socket) {
|
public ClientHandler(Socket socket) {
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
@@ -72,12 +81,17 @@ public class ClientHandler implements Runnable {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if ("MEDIA_DL".equalsIgnoreCase(line)) {
|
if ("MEDIA_DL".equalsIgnoreCase(line)) {
|
||||||
if (this.currentUser.getInternal_uuid() == null) {
|
UUID cu = (currentUser == null ? null : currentUser.getInternal_uuid());
|
||||||
|
logf("MEDIA_DL received. currentUser.internal_uuid=%s", cu);
|
||||||
|
|
||||||
|
if (cu == null) {
|
||||||
|
log("MEDIA_DL rejected: currentUser is null or no internal_uuid");
|
||||||
sendDlErr(dos, "not authorized");
|
sendDlErr(dos, "not authorized");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
handleMediaDownload(dis, dos, this.currentUser.getInternal_uuid());
|
handleMediaDownload(dis, dos, cu);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2925,8 +2939,68 @@ public class ClientHandler implements Runnable {
|
|||||||
|
|
||||||
private static final int MAGIC_DL = 0x4D444D32; // "MDM2"
|
private static final int MAGIC_DL = 0x4D444D32; // "MDM2"
|
||||||
|
|
||||||
|
// private void handleMediaDownload(DataInputStream inBin, DataOutputStream outBin, UUID requesterId) {
|
||||||
|
// try {
|
||||||
|
// int magic = inBin.readInt();
|
||||||
|
// if (magic != MAGIC_DL) { sendDlErr(outBin, "bad magic"); return; }
|
||||||
|
//
|
||||||
|
// int hlen = inBin.readInt();
|
||||||
|
// if (hlen <= 0 || hlen > 64 * 1024) { sendDlErr(outBin, "bad header length"); return; }
|
||||||
|
//
|
||||||
|
// byte[] hb = inBin.readNBytes(hlen);
|
||||||
|
// if (hb.length != hlen) { sendDlErr(outBin, "header truncated"); return; }
|
||||||
|
//
|
||||||
|
// JSONObject hdr = new JSONObject(new String(hb, java.nio.charset.StandardCharsets.UTF_8));
|
||||||
|
// if (!"download".equalsIgnoreCase(hdr.optString("op"))) { sendDlErr(outBin, "bad op"); return; }
|
||||||
|
//
|
||||||
|
// UUID mediaKey = UUID.fromString(hdr.getString("media_key"));
|
||||||
|
// long offset = Math.max(0L, hdr.optLong("offset", 0L));
|
||||||
|
//
|
||||||
|
// MediaRow mr = MessageDatabase.findMediaByKey(mediaKey);
|
||||||
|
// if (mr == null) { sendDlErr(outBin, "not found"); return; }
|
||||||
|
// if (!MessageDatabase.canAccess(requesterId, mr)) { sendDlErr(outBin, "not authorized"); return; }
|
||||||
|
//
|
||||||
|
// java.nio.file.Path path = java.nio.file.Paths.get(mr.storagePath).normalize();
|
||||||
|
// long size = java.nio.file.Files.size(path);
|
||||||
|
// if (offset > size) offset = 0L;
|
||||||
|
//
|
||||||
|
// JSONObject ok = new JSONObject()
|
||||||
|
// .put("status","success")
|
||||||
|
// .put("media_key", mediaKey.toString())
|
||||||
|
// .put("file_name", mr.fileName)
|
||||||
|
// .put("mime_type", mr.mimeType)
|
||||||
|
// .put("file_size", size);
|
||||||
|
//
|
||||||
|
// byte[] okb = ok.toString().getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||||
|
//
|
||||||
|
// outBin.writeInt(MAGIC_DL);
|
||||||
|
// outBin.writeInt(okb.length);
|
||||||
|
// outBin.write(okb);
|
||||||
|
// outBin.writeLong(size - offset);
|
||||||
|
//
|
||||||
|
// try (java.io.InputStream fis = new java.io.BufferedInputStream(java.nio.file.Files.newInputStream(path))) {
|
||||||
|
// if (offset > 0) fis.skipNBytes(offset);
|
||||||
|
// byte[] buf = new byte[8192];
|
||||||
|
// long remain = size - offset;
|
||||||
|
// while (remain > 0) {
|
||||||
|
// int n = fis.read(buf, 0, (int) Math.min(buf.length, remain));
|
||||||
|
// if (n == -1) break;
|
||||||
|
// outBin.write(buf, 0, n);
|
||||||
|
// remain -= n;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// outBin.flush();
|
||||||
|
//
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// try { sendDlErr(outBin, "exception"); } catch (Exception ignored) {}
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
private void handleMediaDownload(DataInputStream inBin, DataOutputStream outBin, UUID requesterId) {
|
private void handleMediaDownload(DataInputStream inBin, DataOutputStream outBin, UUID requesterId) {
|
||||||
try {
|
try {
|
||||||
|
logf("MEDIA_DL start. requester=%s", requesterId);
|
||||||
|
|
||||||
int magic = inBin.readInt();
|
int magic = inBin.readInt();
|
||||||
if (magic != MAGIC_DL) { sendDlErr(outBin, "bad magic"); return; }
|
if (magic != MAGIC_DL) { sendDlErr(outBin, "bad magic"); return; }
|
||||||
|
|
||||||
@@ -2936,15 +3010,38 @@ public class ClientHandler implements Runnable {
|
|||||||
byte[] hb = inBin.readNBytes(hlen);
|
byte[] hb = inBin.readNBytes(hlen);
|
||||||
if (hb.length != hlen) { sendDlErr(outBin, "header truncated"); return; }
|
if (hb.length != hlen) { sendDlErr(outBin, "header truncated"); return; }
|
||||||
|
|
||||||
JSONObject hdr = new JSONObject(new String(hb, java.nio.charset.StandardCharsets.UTF_8));
|
String hdrStr = new String(hb, java.nio.charset.StandardCharsets.UTF_8);
|
||||||
|
logf("MEDIA_DL header: %s", hdrStr);
|
||||||
|
|
||||||
|
JSONObject hdr = new JSONObject(hdrStr);
|
||||||
if (!"download".equalsIgnoreCase(hdr.optString("op"))) { sendDlErr(outBin, "bad op"); return; }
|
if (!"download".equalsIgnoreCase(hdr.optString("op"))) { sendDlErr(outBin, "bad op"); return; }
|
||||||
|
|
||||||
UUID mediaKey = UUID.fromString(hdr.getString("media_key"));
|
UUID mediaKey = UUID.fromString(hdr.getString("media_key"));
|
||||||
long offset = Math.max(0L, hdr.optLong("offset", 0L));
|
long offset = Math.max(0L, hdr.optLong("offset", 0L));
|
||||||
|
logf("Parsed mediaKey=%s offset=%d", mediaKey, offset);
|
||||||
|
|
||||||
MediaRow mr = MessageDatabase.findMediaByKey(mediaKey);
|
MediaRow mr = MessageDatabase.findMediaByKey(mediaKey);
|
||||||
if (mr == null) { sendDlErr(outBin, "not found"); return; }
|
if (mr == null) { sendDlErr(outBin, "not found"); return; }
|
||||||
if (!MessageDatabase.canAccess(requesterId, mr)) { sendDlErr(outBin, "not authorized"); return; }
|
|
||||||
|
logf("MediaRow: chatType=%s chatId=%s sender=%s receiver=%s storage=%s",
|
||||||
|
mr.chatType, mr.chatId, mr.senderId, mr.receiverId, mr.storagePath);
|
||||||
|
|
||||||
|
// تست مستقیم دیتابیس (موقت برای دیباگ)
|
||||||
|
try (java.sql.Connection c = ConnectionDb.connect();
|
||||||
|
java.sql.PreparedStatement st = c.prepareStatement(
|
||||||
|
"SELECT 1 FROM channel_subscribers WHERE channel_id = ? AND user_id = ? LIMIT 1")) {
|
||||||
|
st.setObject(1, mr.chatId, java.sql.Types.OTHER);
|
||||||
|
st.setObject(2, requesterId, java.sql.Types.OTHER);
|
||||||
|
boolean direct;
|
||||||
|
try (java.sql.ResultSet r = st.executeQuery()) { direct = r.next(); }
|
||||||
|
logf("[DL] direct channel membership ch=%s user=%s => %s", mr.chatId, requesterId, direct);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logf("[DL] direct membership check ERROR: %s", e.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean allowed = MessageDatabase.canAccess(requesterId, mr);
|
||||||
|
logf("canAccess(..) -> %s", allowed);
|
||||||
|
if (!allowed) { sendDlErr(outBin, "not authorized"); return; }
|
||||||
|
|
||||||
java.nio.file.Path path = java.nio.file.Paths.get(mr.storagePath).normalize();
|
java.nio.file.Path path = java.nio.file.Paths.get(mr.storagePath).normalize();
|
||||||
long size = java.nio.file.Files.size(path);
|
long size = java.nio.file.Files.size(path);
|
||||||
@@ -2963,6 +3060,7 @@ public class ClientHandler implements Runnable {
|
|||||||
outBin.writeInt(okb.length);
|
outBin.writeInt(okb.length);
|
||||||
outBin.write(okb);
|
outBin.write(okb);
|
||||||
outBin.writeLong(size - offset);
|
outBin.writeLong(size - offset);
|
||||||
|
logf("Sending OK header. file=%s size=%d offset=%d", mr.fileName, size, offset);
|
||||||
|
|
||||||
try (java.io.InputStream fis = new java.io.BufferedInputStream(java.nio.file.Files.newInputStream(path))) {
|
try (java.io.InputStream fis = new java.io.BufferedInputStream(java.nio.file.Files.newInputStream(path))) {
|
||||||
if (offset > 0) fis.skipNBytes(offset);
|
if (offset > 0) fis.skipNBytes(offset);
|
||||||
@@ -2976,6 +3074,7 @@ public class ClientHandler implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
outBin.flush();
|
outBin.flush();
|
||||||
|
log("MEDIA_DL done.");
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
@@ -73,4 +73,29 @@ public class ChannelPermissionUtil {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean isUserInChannel(UUID userId, UUID channelId) {
|
||||||
|
final String SQL = "SELECT 1 FROM channel_subscribers WHERE channel_id = ? AND user_id = ? LIMIT 1";
|
||||||
|
try (Connection conn = ConnectionDb.connect();
|
||||||
|
PreparedStatement ps = conn.prepareStatement(SQL)) {
|
||||||
|
|
||||||
|
ps.setObject(1, channelId, java.sql.Types.OTHER); // 👈 مهم برای Postgres UUID
|
||||||
|
ps.setObject(2, userId, java.sql.Types.OTHER);
|
||||||
|
|
||||||
|
System.out.println("[SQL] isUserInChannel ch=" + channelId + " user=" + userId
|
||||||
|
+ " db=" + conn.getMetaData().getURL());
|
||||||
|
|
||||||
|
try (ResultSet rs = ps.executeQuery()) {
|
||||||
|
boolean ok = rs.next();
|
||||||
|
System.out.println("[SQL] isUserInChannel -> " + ok);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user