I recently took on a project to automate email-related tasks for an acquaintance’s company. The task involved recognizing invoice PDF files and uploading them to Google Sheets using Google Apps Script (GAS).

During this process, post-processing was required to move the completed emails to another folder. Initially, I wanted to use the official NaverWorks API to implement the email moving feature, but the NaverWorks email API does not have a “Move” action.

Here is a summary of the trials and errors I encountered and how I resolved the issue using IMAP.


1. First Attempt: Using the Automatic Classification (Filter) API and Its Limitations

Since there was no email moving API, I came up with an alternative approach to dynamically control the email automatic classification filter rules.

  • Workflow: Add filter rule -> Retrieve filter rules -> Delete filter rule

However, the automatic classification filtering conditions do not allow specifying a specific email ID (Mail ID). Because it was impossible to target and move a specific single email, this approach could not be used.

Ultimately, I chose to connect directly to the mail server using the IMAP protocol to move the email.


2. Identifying Emails: Matching Sent Time down to Seconds

When using IMAP, I had to find the exact email that the GAS batch process had just processed, but I couldn’t pass the email key directly.

This was because when querying the email using the NaverWorks API, the mail key to be used in the IMAP standard came back as undefined. Therefore, I matched the email sent times instead.

After searching by sender & subject, I compared the sent times down to the second, allowing a margin of error of 1 to 2 seconds to account for slight latency, which allowed me to pinpoint the target email accurately.


3. Second Error: Mailbox Non-ASCII Characters Recognition

When I attempted to access the Korean folder name using Python’s built-in imaplib library, an error occurred.

import imaplib

mail = imaplib.IMAP4_SSL("imap.naverworks.com")
mail.login("your-email@domain.com", "your-password")

# Attempting to select non-ASCII folder name
mail.select("발주서_처리대기")

The above code produces the following error:

UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)

Root Cause Analysis

By default, imaplib attempts to encode commands with the ASCII codec when communicating via socket. Therefore, passing a non-ASCII string raises an encoding error.


4. Third Error: UTF-8 Byte Transmission Failure

So I tried encoding the non-ASCII string directly into UTF-8 bytes and sending it.

folder_name = "발주서_처리대기".encode("utf-8")
mail.select(folder_name)

In this case, the encoding error does not occur, but the mail server returns a folder not found error.

imaplib.IMAP4.error: select failed: [NONEXISTENT] Mailbox doesn't exist

Root Cause

Under the IMAP protocol (RFC 3501) specifications, unless a UTF8=ACCEPT extension handshake is agreed upon beforehand, mailbox names containing non-ASCII characters cannot be processed as regular UTF-8 byte streams. The NaverWorks IMAP server failed to recognize the sent UTF-8 bytes as a valid folder name.


5. Solution: Applying Modified UTF-7 Encoding

According to the IMAP standard specifications, multi-language mailbox names must be encoded using the Modified UTF-7 method before transmission.

Modified UTF-7 is a standard specification that converts non-ASCII character blocks into Modified Base64, prefixing them with & and suffixing them with -, and replacing the Base64 padding character / with ,.

To handle this in a Python environment, I wrote a translation helper function as follows:

import binascii

def encode_modified_utf7(s: str) -> bytes:
    """
    Converts a normal string into an IMAP Modified UTF-7 byte string
    """
    res = []
    r = []
    
    for c in s:
        if 0x20 <= ord(c) <= 0x7E and c != '&':
            if r:
                res.append(f"&{binascii.b2a_base64(''.join(r).encode('utf-16be')).decode('ascii').rstrip('=\n').replace('/', ',')}-")
                r = []
            res.append(c)
        elif c == '&':
            if r:
                res.append(f"&{binascii.b2a_base64(''.join(r).encode('utf-16be')).decode('ascii').rstrip('=\n').replace('/', ',')}-")
                r = []
            res.append('&-')
        else:
            r.append(c)
            
    if r:
        res.append(f"&{binascii.b2a_base64(''.join(r).encode('utf-16be')).decode('ascii').rstrip('=\n').replace('/', ',')}-")
        
    return "".join(res).encode('ascii')

Using this helper function, I wrote the final code to move emails between non-ASCII folder names:

# Convert non-ASCII folder names
src_folder = encode_modified_utf7("발주서_처리대기")
dest_folder = encode_modified_utf7("발주서_처리완료")

# 1. Select the source folder
mail.select(src_folder)

# 2. Search for the target email using conditions like the sent time match
status, data = mail.search(None, "ALL")
mail_ids = data[0].split()

if mail_ids:
    target_id = mail_ids[-1]
    
    # 3. Copy the email to the destination folder and flag the original email for deletion
    result, apply_data = mail.copy(target_id, dest_folder)
    
    if result == 'OK':
        mail.store(target_id, '+FLAGS', '\\Deleted')
        mail.expunge()
        print("Email move completed")

By switching to this method, I was able to overcome the limitations of the NaverWorks API and successfully build the email moving automation linked with the GAS batch process.

(Actually, it would have been much simpler if I had just changed the mailbox names to English…)